Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qhttpnetworkconnectionchannel.cpp
Go to the documentation of this file.
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
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
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:
35static const int reconnectAttemptsDefault = 3;
36static const char keepAliveIdleOption[] = "QT_QNAM_TCP_KEEPIDLE";
37static const char keepAliveIntervalOption[] = "QT_QNAM_TCP_KEEPINTVL";
38static const char keepAliveCountOption[] = "QT_QNAM_TCP_KEEPCNT";
39static const int TCP_KEEPIDLE_DEF = 60;
40static const int TCP_KEEPINTVL_DEF = 10;
41static const int TCP_KEEPCNT_DEF = 5;
42
44 : socket(nullptr)
45 , ssl(false)
46 , isInitialized(false)
48 , reply(nullptr)
49 , written(0)
50 , bytesTotal(0)
51 , resendCurrent(false)
52 , lastStatus(0)
53 , pendingEncrypt(false)
57 , protocolHandler(nullptr)
58#ifndef QT_NO_SSL
59 , ignoreAllSslErrors(false)
60#endif
63 , connection(nullptr)
64{
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}
68
70{
71#ifndef QT_NO_SSL
72 if (connection->d_func()->encrypt)
73 socket = new QSslSocket;
74#if QT_CONFIG(localserver)
75 else if (connection->d_func()->isLocalSocket)
76 socket = new QLocalSocket;
77#endif
78 else
79 socket = new QTcpSocket;
80#else
81 socket = new QTcpSocket;
82#endif
83#ifndef QT_NO_NETWORKPROXY
84 // Set by QNAM anyway, but let's be safe here
85 if (auto s = qobject_cast<QAbstractSocket *>(socket))
86 s->setProxy(QNetworkProxy::NoProxy);
87#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 QObject::connect(socket, &QIODevice::bytesWritten,
93 this, &QHttpNetworkConnectionChannel::_q_bytesWritten,
94 Qt::DirectConnection);
95 QObject::connect(socket, &QIODevice::readyRead,
96 this, &QHttpNetworkConnectionChannel::_q_readyRead,
97 Qt::DirectConnection);
98
99
100 QSocketAbstraction::visit([this](auto *socket){
101 using SocketType = std::remove_pointer_t<decltype(socket)>;
102 QObject::connect(socket, &SocketType::connected,
103 this, &QHttpNetworkConnectionChannel::_q_connected,
104 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 QObject::connect(socket, &SocketType::disconnected,
113 this, &QHttpNetworkConnectionChannel::_q_disconnected,
114 Qt::DirectConnection);
115 if constexpr (std::is_same_v<SocketType, QAbstractSocket>) {
116 QObject::connect(socket, &QAbstractSocket::errorOccurred,
117 this, &QHttpNetworkConnectionChannel::_q_error,
118 Qt::DirectConnection);
119#if QT_CONFIG(localserver)
120 } else if constexpr (std::is_same_v<SocketType, QLocalSocket>) {
121 auto convertAndForward = [this](QLocalSocket::LocalSocketError error) {
122 _q_error(static_cast<QAbstractSocket::SocketError>(error));
123 };
124 QObject::connect(socket, &SocketType::errorOccurred,
125 this, std::move(convertAndForward),
126 Qt::DirectConnection);
127#endif
128 }
129 }, socket);
130
131
132
133#ifndef QT_NO_NETWORKPROXY
134 if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
135 QObject::connect(s, &QAbstractSocket::proxyAuthenticationRequired,
136 this, &QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired,
137 Qt::DirectConnection);
138 }
139#endif
140
141#ifndef QT_NO_SSL
142 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
143 if (sslSocket) {
144 // won't be a sslSocket if encrypt is false
145 QObject::connect(sslSocket, &QSslSocket::encrypted,
146 this, &QHttpNetworkConnectionChannel::_q_encrypted,
147 Qt::DirectConnection);
148 QObject::connect(sslSocket, &QSslSocket::sslErrors,
149 this, &QHttpNetworkConnectionChannel::_q_sslErrors,
150 Qt::DirectConnection);
151 QObject::connect(sslSocket, &QSslSocket::preSharedKeyAuthenticationRequired,
152 this, &QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired,
153 Qt::DirectConnection);
154 QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten,
155 this, &QHttpNetworkConnectionChannel::_q_encryptedBytesWritten,
156 Qt::DirectConnection);
157
158 // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
160 sslSocket->ignoreSslErrors();
161
162 if (!ignoreSslErrorsList.isEmpty())
163 sslSocket->ignoreSslErrors(ignoreSslErrorsList);
164 // AXIVION ENABLE Qt-Security-QSslSocketIgnoreSslErrors
165
166 if (sslConfiguration && !sslConfiguration->isNull())
167 sslSocket->setSslConfiguration(*sslConfiguration);
168 } else {
169#endif // !QT_NO_SSL
170 if (connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2)
171 protocolHandler.reset(new QHttpProtocolHandler(this));
172#ifndef QT_NO_SSL
173 }
174#endif
175
176#ifndef QT_NO_NETWORKPROXY
177 if (auto *s = qobject_cast<QAbstractSocket *>(socket);
178 s && proxy.type() != QNetworkProxy::NoProxy) {
179 s->setProxy(proxy);
180 }
181#endif
182 isInitialized = true;
183}
184
185
187{
188 if (state == QHttpNetworkConnectionChannel::ClosingState)
189 return;
190
191 if (!socket)
192 state = QHttpNetworkConnectionChannel::IdleState;
193 else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
194 state = QHttpNetworkConnectionChannel::IdleState;
195 else
196 state = QHttpNetworkConnectionChannel::ClosingState;
197
198 // pendingEncrypt must only be true in between connected and encrypted states
199 pendingEncrypt = false;
200
201 if (socket) {
202 // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
203 // there is no socket yet.
204 socket->close();
205 }
206}
207
208
210{
211 if (!socket)
212 state = QHttpNetworkConnectionChannel::IdleState;
213 else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
214 state = QHttpNetworkConnectionChannel::IdleState;
215 else
216 state = QHttpNetworkConnectionChannel::ClosingState;
217
218 // pendingEncrypt must only be true in between connected and encrypted states
219 pendingEncrypt = false;
220
221 if (socket) {
222 // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
223 // there is no socket yet.
224 auto callAbort = [](auto *s) {
225 s->abort();
226 };
227 QSocketAbstraction::visit(callAbort, socket);
228 }
229}
230
231
233{
234 Q_ASSERT(protocolHandler);
237 return;
238 }
239 protocolHandler->sendRequest();
240}
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 */
249{
250 QMetaObject::invokeMethod(this, [this] {
251 if (reply)
252 sendRequest();
253 }, Qt::ConnectionType::QueuedConnection);
254}
255
256void QHttpNetworkConnectionChannel::_q_receiveReply()
257{
258 Q_ASSERT(protocolHandler);
261 return;
262 }
263 protocolHandler->_q_receiveReply();
264}
265
267{
268 Q_ASSERT(protocolHandler);
270 needInvokeReadyRead = true;
271 return;
272 }
273 protocolHandler->_q_readyRead();
274}
275
276// called when unexpectedly reading a -1 or when data is expected but socket is closed
278{
279 Q_ASSERT(reply);
280 if (reconnectAttempts <= 0 || !request.methodIsIdempotent()) {
281 // too many errors reading/receiving/parsing the status, close the socket and emit error
283 close();
284 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
285 emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
286 reply = nullptr;
287 if (protocolHandler)
288 protocolHandler->setReply(nullptr);
289 request = QHttpNetworkRequest();
290 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
291 } else {
293 reply->d_func()->clear();
294 reply->d_func()->connection = connection;
295 reply->d_func()->connectionChannel = this;
297 }
298}
299
301{
302 if (!isInitialized)
303 init();
304
305 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 if (socketState == QAbstractSocket::ClosingState ||
313 (socketState != QAbstractSocket::UnconnectedState && !socket->isOpen())) {
314 if (reply)
315 resendCurrent = true;
316 return false;
317 }
318
319 // already trying to connect?
320 if (socketState == QAbstractSocket::HostLookupState ||
321 socketState == QAbstractSocket::ConnectingState) {
322 return false;
323 }
324
325 // make sure that this socket is in a connected state, if not initiate
326 // connection to the host.
327 if (socketState != QAbstractSocket::ConnectedState) {
328 // connect to the host if not already connected.
329 state = QHttpNetworkConnectionChannel::ConnectingState;
331
332 // reset state
335 proxyCredentialsSent = false;
336 authenticator.detach();
337 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
338 priv->hasFailed = false;
339 proxyAuthenticator.detach();
340 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
341 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 priv = QAuthenticatorPrivate::getPrivate(authenticator);
350 if (priv && priv->phase == QAuthenticatorPrivate::Done)
351 priv->phase = QAuthenticatorPrivate::Start;
352 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
353 if (priv && priv->phase == QAuthenticatorPrivate::Done)
354 priv->phase = QAuthenticatorPrivate::Start;
355
356 QString connectHost = connection->d_func()->hostName;
357 quint16 connectPort = connection->d_func()->port;
358
359 QHttpNetworkReply *potentialReply = connection->d_func()->predictNextRequestsReply();
360 if (potentialReply) {
361 QMetaObject::invokeMethod(potentialReply, "socketStartedConnecting", Qt::QueuedConnection);
362 } else if (!h2RequestsToSend.isEmpty()) {
363 QMetaObject::invokeMethod(std::as_const(h2RequestsToSend).first().second, "socketStartedConnecting", Qt::QueuedConnection);
364 }
365
366#ifndef QT_NO_NETWORKPROXY
367 // HTTPS always use transparent proxy.
368 if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
369 connectHost = connection->d_func()->networkProxy.hostName();
370 connectPort = connection->d_func()->networkProxy.port();
371 }
372 if (auto *abSocket = qobject_cast<QAbstractSocket *>(socket);
373 abSocket && abSocket->proxy().type() == QNetworkProxy::HttpProxy) {
374 // Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
375 QByteArray value;
376 // ensureConnection is called before any request has been assigned, but can also be
377 // called again if reconnecting
378 if (request.url().isEmpty()) {
379 if (connection->connectionType()
380 == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
381 || (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
382 && !h2RequestsToSend.isEmpty())) {
383 value = std::as_const(h2RequestsToSend).first().first.headerField("user-agent");
384 } else {
385 value = connection->d_func()->predictNextRequest().headerField("user-agent");
386 }
387 } else {
388 value = request.headerField("user-agent");
389 }
390 if (!value.isEmpty()) {
391 QNetworkProxy proxy(abSocket->proxy());
392 auto h = proxy.headers();
393 h.replaceOrAppend(QHttpHeaders::WellKnownHeader::UserAgent, value);
394 proxy.setHeaders(std::move(h));
395 abSocket->setProxy(proxy);
396 }
397 }
398#endif
399 if (ssl) {
400#ifndef QT_NO_SSL
401 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 if (auto ctx = connection->sslContext())
407 QSslSocketPrivate::checkSettingSslContext(sslSocket, std::move(ctx));
408
409 sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName);
410 sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
411 // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
413 sslSocket->ignoreSslErrors();
414 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 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 } else {
428 // In case of no proxy we can use the Unbuffered QTcpSocket
429#ifndef QT_NO_NETWORKPROXY
430 if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
431 && connection->cacheProxy().type() == QNetworkProxy::NoProxy
432 && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
433#endif
434 if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
435 s->connectToHost(connectHost, connectPort,
436 QIODevice::ReadWrite | QIODevice::Unbuffered,
437 networkLayerPreference);
438 // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
439 s->setReadBufferSize(1 * 1024);
440#if QT_CONFIG(localserver)
441 } else if (auto *s = qobject_cast<QLocalSocket *>(socket)) {
442 s->connectToServer(connectHost);
443#endif
444 }
445#ifndef QT_NO_NETWORKPROXY
446 } else {
447 auto *s = qobject_cast<QAbstractSocket *>(socket);
448 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 s->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
453 s->setReadBufferSize(64 * 1024);
454 }
455#endif
456 }
457 return false;
458 }
459
460 // This code path for ConnectedState
461 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 return false;
465 }
466
467 return true;
468}
469
471{
472 Q_ASSERT(reply);
473
474 if (!reply) {
475 qWarning("QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.io/");
476 return;
477 }
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 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
484 && !ssl && !switchedToHttp2) {
485 if (Http2::is_protocol_upgraded(*reply)) {
486 switchedToHttp2 = true;
487 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 QMetaObject::invokeMethod(this, [oldHandler = std::move(protocolHandler)]() mutable {
495 oldHandler.reset();
496 }, Qt::QueuedConnection);
497
498 connection->fillHttp2Queue();
499 protocolHandler.reset(new QHttp2ProtocolHandler(this));
500 QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
501 QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection);
502 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
503 return;
504 } else {
505 // Ok, whatever happened, we do not try HTTP/2 anymore ...
506 connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
507 connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
508 }
509 }
510
511 // while handling 401 & 407, we might reset the status code, so save this.
512 bool emitFinished = reply->d_func()->shouldEmitSignals();
513 bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
515
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 if (reply && emitFinished)
523 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.
529
530 // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
531 if (state != QHttpNetworkConnectionChannel::ClosingState)
532 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 if (!resendCurrent) {
540 request = QHttpNetworkRequest();
541 reply = nullptr;
542 protocolHandler->setReply(nullptr);
543 }
544
545 // move next from pipeline to current request
546 if (!alreadyPipelinedRequests.isEmpty()) {
547 if (resendCurrent || connectionCloseEnabled || QSocketAbstraction::socketState(socket) != QAbstractSocket::ConnectedState) {
548 // move the pipelined ones back to the main queue
550 close();
551 } else {
552 // there were requests pipelined in and we can continue
553 HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
554
555 request = messagePair.first;
556 reply = messagePair.second;
557 protocolHandler->setReply(messagePair.second);
558 state = QHttpNetworkConnectionChannel::ReadingState;
559 resendCurrent = false;
560
561 written = 0; // message body, excluding the header, irrelevant here
562 bytesTotal = 0; // message body total, excluding the header, irrelevant here
563
564 // pipeline even more
565 connection->d_func()->fillPipeline(socket);
566
567 // continue reading
568 //_q_receiveReply();
569 // this was wrong, allDone gets called from that function anyway.
570 }
571 } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
572 // this is weird. we had nothing pipelined but still bytes available. better close it.
573 close();
574
575 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
576 } else if (alreadyPipelinedRequests.isEmpty()) {
577 if (connectionCloseEnabled)
578 if (QSocketAbstraction::socketState(socket) != QAbstractSocket::UnconnectedState)
579 close();
580 if (qobject_cast<QHttpNetworkConnection*>(connection))
581 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
582 }
583}
584
586{
587 Q_ASSERT(reply);
588 // detect HTTP Pipelining support
589 QByteArray serverHeaderField;
590 if (
591 // check for HTTP/1.1
592 (reply->majorVersion() == 1 && reply->minorVersion() == 1)
593 // check for not having connection close
594 && (!reply->d_func()->isConnectionCloseEnabled())
595 // check if it is still connected
596 && (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 && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
600 && (!serverHeaderField.contains("Microsoft-IIS/5."))
601 && (!serverHeaderField.contains("Netscape-Enterprise/3."))
602 // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
603 && (!serverHeaderField.contains("WebLogic"))
604 && (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
605 ) {
607 } else {
609 }
610}
611
612// called when the connection broke and we need to queue some pipelined requests again
614{
615 for (int i = 0; i < alreadyPipelinedRequests.size(); i++)
616 connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
617 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 if (qobject_cast<QHttpNetworkConnection*>(connection))
623 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
624}
625
627{
628 Q_ASSERT(socket);
629 Q_ASSERT(reply);
630
631 int statusCode = reply->statusCode();
632 bool resend = false;
633
634 switch (statusCode) {
635 case 301:
636 case 302:
637 case 303:
638 case 305:
639 case 307:
640 case 308: {
641 // Parse the response headers and get the "location" url
642 QUrl redirectUrl = connection->d_func()->parseRedirectResponse(socket, reply);
643 if (redirectUrl.isValid())
644 reply->setRedirectUrl(redirectUrl);
645
646 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 } else if (qobject_cast<QHttpNetworkConnection *>(connection)) {
652 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
653 }
654 break;
655 }
656 case 401: // auth required
657 case 407: // proxy auth required
658 if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
659 if (resend) {
661 break;
662
663 reply->d_func()->eraseData();
664
665 if (alreadyPipelinedRequests.isEmpty()) {
666 // this does a re-send without closing the connection
667 resendCurrent = true;
668 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
669 } else {
670 // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
672 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
673 }
674 } else {
675 //authentication cancelled, close the channel.
676 close();
677 }
678 } else {
679 emit reply->headerChanged();
680 emit reply->readyRead();
681 QNetworkReply::NetworkError errorCode = (statusCode == 407)
682 ? QNetworkReply::ProxyAuthenticationRequiredError
683 : QNetworkReply::AuthenticationRequiredError;
684 reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
685 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
686 }
687 break;
688 default:
689 if (qobject_cast<QHttpNetworkConnection*>(connection))
690 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
691 }
692}
693
695{
696 if (!reply) {
697 //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
698 return false;
699 }
700 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
701 || 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 } else if (QNonContiguousByteDevice *uploadByteDevice = request.uploadByteDevice()) {
706 if (!uploadByteDevice->reset()) {
707 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
708 return false;
709 }
710 written = 0;
711 }
712 return true;
713}
714
715#ifndef QT_NO_NETWORKPROXY
716
717void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
718{
719 if (auto *s = qobject_cast<QAbstractSocket *>(socket))
720 s->setProxy(networkProxy);
721
722 proxy = networkProxy;
723}
724
725#endif
726
727#ifndef QT_NO_SSL
728
730{
731 // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
732 if (socket)
733 static_cast<QSslSocket *>(socket)->ignoreSslErrors();
734 // AXIVION ENABLE Qt-Security-QSslSocketIgnoreSslErrors
735
736 ignoreAllSslErrors = true;
737}
738
739
740void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors)
741{
742 // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
743 if (socket)
744 static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors);
745 // AXIVION ENABLE Qt-Security-QSslSocketIgnoreSslErrors
746
747 ignoreSslErrorsList = errors;
748}
749
750void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config)
751{
752 if (socket)
753 static_cast<QSslSocket *>(socket)->setSslConfiguration(config);
754
755 if (sslConfiguration)
756 *sslConfiguration = config;
757 else
758 sslConfiguration = QSslConfiguration(config);
759}
760
761#endif
762
764{
765 // this is only called for simple GET
766
767 QHttpNetworkRequest &request = pair.first;
768 QHttpNetworkReply *reply = pair.second;
769 reply->d_func()->clear();
770 reply->d_func()->connection = connection;
771 reply->d_func()->connectionChannel = this;
772 reply->d_func()->autoDecompress = request.d->autoDecompress;
773 reply->d_func()->pipeliningUsed = true;
774
775#ifndef QT_NO_NETWORKPROXY
776 pipeline.append(QHttpNetworkRequestPrivate::header(request,
777 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
778#else
779 pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
780#endif
781
782 alreadyPipelinedRequests.append(pair);
783
784 // pipelineFlush() needs to be called at some point afterwards
785}
786
788{
789 if (pipeline.isEmpty())
790 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 socket->write(pipeline);
798 pipeline.clear();
799}
800
801
803{
805 close();
806 if (reply)
807 resendCurrent = true;
808 if (qobject_cast<QHttpNetworkConnection*>(connection))
809 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
810}
811
813{
815 if (reply)
816 resendCurrent = true;
817 if (qobject_cast<QHttpNetworkConnection*>(connection))
818 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
819}
820
822{
823 return (state & QHttpNetworkConnectionChannel::BusyState);
824}
825
827{
828 return (state & QHttpNetworkConnectionChannel::WritingState);
829}
830
832{
833 return (state & QHttpNetworkConnectionChannel::WaitingState);
834}
835
837{
838 return (state & QHttpNetworkConnectionChannel::ReadingState);
839}
840
842{
843 if (!protocolHandler)
844 return nullptr;
845 const auto type = connection->connectionType();
846 if (type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
847 || (type == QHttpNetworkConnection::ConnectionTypeHTTP2 && switchedToHttp2)) {
848 return static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
849 }
850 return nullptr;
851}
852
854{
855 Q_UNUSED(bytes);
856 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 return;
860 }
861
862 // bytes have been written to the socket. write even more of them :)
865 // otherwise we do nothing
866}
867
869{
870 if (state == QHttpNetworkConnectionChannel::ClosingState) {
871 state = QHttpNetworkConnectionChannel::IdleState;
872 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
873 return;
874 }
875
876 // read the available data before closing (also done in _q_error for other codepaths)
877 if ((isSocketWaiting() || isSocketReading()) && socket->bytesAvailable()) {
878 if (reply) {
879 state = QHttpNetworkConnectionChannel::ReadingState;
880 _q_receiveReply();
881 }
882 } 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 _q_receiveReply();
886 } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
887 // re-sending request because the socket was in ClosingState
888 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
889 }
890 state = QHttpNetworkConnectionChannel::IdleState;
891 if (alreadyPipelinedRequests.size()) {
892 // If nothing was in a pipeline, no need in calling
893 // _q_startNextRequest (which it does):
895 }
896
897 pendingEncrypt = false;
898}
899
900
902{
903 // For the Happy Eyeballs we need to check if this is the first channel to connect.
904 if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::HostLookupPending || connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4or6) {
905 if (connection->d_func()->delayedConnectionTimer.isActive())
906 connection->d_func()->delayedConnectionTimer.stop();
907 if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
908 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
909 else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
910 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
911 else {
912 if (absSocket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
913 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
914 else
915 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
916 }
917 connection->d_func()->networkLayerDetected(networkLayerPreference);
918 if (connection->d_func()->activeChannelCount > 1 && !connection->d_func()->encrypt)
919 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
920 } else {
921 bool anyProtocol = networkLayerPreference == QAbstractSocket::AnyIPProtocol;
922 if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4)
923 && (networkLayerPreference != QAbstractSocket::IPv4Protocol && !anyProtocol))
924 || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6)
925 && (networkLayerPreference != QAbstractSocket::IPv6Protocol && !anyProtocol))) {
926 close();
927 // This is the second connection so it has to be closed and we can schedule it for another request.
928 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
929 return;
930 }
931 //The connections networkLayerState had already been decided.
932 }
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 absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
945
946 QTcpKeepAliveConfiguration keepAliveConfig = connection->tcpKeepAliveParameters();
947
948 auto getKeepAliveValue = [](int configValue,
949 const char* envName,
950 int defaultValue) {
951 if (configValue > 0)
952 return configValue;
953 return static_cast<int>(qEnvironmentVariableIntegerValue(envName).value_or(defaultValue));
954 };
955
956 int kaIdleOption = getKeepAliveValue(keepAliveConfig.idleTimeBeforeProbes.count(), keepAliveIdleOption, TCP_KEEPIDLE_DEF);
957 int kaIntervalOption = getKeepAliveValue(keepAliveConfig.intervalBetweenProbes.count(), keepAliveIntervalOption, TCP_KEEPINTVL_DEF);
958 int kaCountOption = getKeepAliveValue(keepAliveConfig.probeCount, keepAliveCountOption, TCP_KEEPCNT_DEF);
959 absSocket->setSocketOption(QAbstractSocket::KeepAliveIdleOption, kaIdleOption);
960 absSocket->setSocketOption(QAbstractSocket::KeepAliveIntervalOption, kaIntervalOption);
961 absSocket->setSocketOption(QAbstractSocket::KeepAliveCountOption, kaCountOption);
962
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 if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
968#ifndef QT_NO_SSL
969 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 if (auto socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(absSocket)))
973 connection->setSslContext(std::move(socketSslContext));
974 }
975#endif
976 } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
977 state = QHttpNetworkConnectionChannel::IdleState;
978 protocolHandler.reset(new QHttp2ProtocolHandler(this));
979 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 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
983 }
984 } else {
985 state = QHttpNetworkConnectionChannel::IdleState;
986 const bool tryProtocolUpgrade = connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2;
987 if (tryProtocolUpgrade) {
988 // For HTTP/1.1 it's already created and never reset.
989 protocolHandler.reset(new QHttpProtocolHandler(this));
990 }
991 switchedToHttp2 = false;
992
993 if (!reply)
994 connection->d_func()->dequeueRequest(absSocket);
995
996 if (reply) {
997 if (tryProtocolUpgrade) {
998 // Let's augment our request with some magic headers and try to
999 // switch to HTTP/2.
1000 Http2::appendProtocolUpgradeHeaders(connection->http2Parameters(), &request);
1001 }
1003 }
1004 }
1005}
1006
1007#if QT_CONFIG(localserver)
1008void QHttpNetworkConnectionChannel::_q_connected_local_socket(QLocalSocket *localSocket)
1009{
1010 state = QHttpNetworkConnectionChannel::IdleState;
1011 if (!reply) // No reply object, try to dequeue a request (which is paired with a reply):
1012 connection->d_func()->dequeueRequest(localSocket);
1013 if (reply)
1014 sendRequest();
1015}
1016#endif
1017
1019{
1020 if (auto *s = qobject_cast<QAbstractSocket *>(socket))
1021 _q_connected_abstract_socket(s);
1022#if QT_CONFIG(localserver)
1023 else if (auto *s = qobject_cast<QLocalSocket *>(socket))
1024 _q_connected_local_socket(s);
1025#endif
1026}
1027
1028void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
1029{
1030 if (!socket)
1031 return;
1032 QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
1033
1034 switch (socketError) {
1035 case QAbstractSocket::HostNotFoundError:
1036 errorCode = QNetworkReply::HostNotFoundError;
1037 break;
1038 case QAbstractSocket::ConnectionRefusedError:
1039 errorCode = QNetworkReply::ConnectionRefusedError;
1040#ifndef QT_NO_NETWORKPROXY
1041 if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl)
1042 errorCode = QNetworkReply::ProxyConnectionRefusedError;
1043#endif
1044 break;
1045 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 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 if (auto *h2Handler = h2ProtocolHandler())
1057 h2Handler->handleConnectionClosure();
1058 return;
1059 } 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 if (!pendingEncrypt && reconnectAttempts-- > 0) {
1067 return;
1068 } else {
1069 errorCode = QNetworkReply::RemoteHostClosedError;
1070 }
1071 } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1072 if (!reply)
1073 break;
1074
1075 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 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1079 return;
1080 }
1081 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 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1086 return;
1087 }
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 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 reply->setReadBufferSize(0);
1095 reply->setDownstreamLimited(false);
1096 _q_receiveReply();
1097 if (!reply) {
1098 // No more reply assigned after the previous call? Then it had been finished successfully.
1100 state = QHttpNetworkConnectionChannel::IdleState;
1101 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1102 return;
1103 }
1104 }
1105
1106 errorCode = QNetworkReply::RemoteHostClosedError;
1107 } else {
1108 errorCode = QNetworkReply::RemoteHostClosedError;
1109 }
1110 break;
1111 case QAbstractSocket::SocketTimeoutError:
1112 // try to reconnect/resend before sending an error.
1113 if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1115 return;
1116 }
1117 errorCode = QNetworkReply::TimeoutError;
1118 break;
1119 case QAbstractSocket::ProxyConnectionRefusedError:
1120 errorCode = QNetworkReply::ProxyConnectionRefusedError;
1121 break;
1122 case QAbstractSocket::ProxyAuthenticationRequiredError:
1123 errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1124 break;
1125 case QAbstractSocket::SslHandshakeFailedError:
1126 errorCode = QNetworkReply::SslHandshakeFailedError;
1127 break;
1128 case QAbstractSocket::ProxyConnectionClosedError:
1129 // try to reconnect/resend before sending an error.
1130 if (reconnectAttempts-- > 0) {
1132 return;
1133 }
1134 errorCode = QNetworkReply::ProxyConnectionClosedError;
1135 break;
1136 case QAbstractSocket::ProxyConnectionTimeoutError:
1137 // try to reconnect/resend before sending an error.
1138 if (reconnectAttempts-- > 0) {
1140 return;
1141 }
1142 errorCode = QNetworkReply::ProxyTimeoutError;
1143 break;
1144 default:
1145 // all other errors are treated as NetworkError
1146 errorCode = QNetworkReply::UnknownNetworkError;
1147 break;
1148 }
1149 QPointer<QHttpNetworkConnection> that = connection;
1150 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 if (!connection->d_func()->shouldEmitChannelError(socket))
1155 return;
1156
1157 // emit error for all waiting replies
1158 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 if (reply)
1163 else
1164 connection->d_func()->dequeueRequest(socket);
1165
1166 if (reply) {
1167 reply->d_func()->errorString = errorString;
1168 reply->d_func()->httpErrorCode = errorCode;
1169 emit reply->finishedWithError(errorCode, errorString);
1170 reply = nullptr;
1171 if (protocolHandler)
1172 protocolHandler->setReply(nullptr);
1173 }
1174 } while (!connection->d_func()->highPriorityQueue.isEmpty()
1175 || !connection->d_func()->lowPriorityQueue.isEmpty());
1176
1177 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1178 || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1179 const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
1180 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1181 // emit error for all replies
1182 QHttpNetworkReply *currentReply = httpMessagePair.second;
1183 currentReply->d_func()->errorString = errorString;
1184 currentReply->d_func()->httpErrorCode = errorCode;
1185 Q_ASSERT(currentReply);
1186 emit currentReply->finishedWithError(errorCode, errorString);
1187 }
1188 }
1189
1190 // send the next request
1191 QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1192
1193 if (that) {
1194 //signal emission triggered event loop
1195 if (!socket)
1196 state = QHttpNetworkConnectionChannel::IdleState;
1197 else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
1198 state = QHttpNetworkConnectionChannel::IdleState;
1199 else
1200 state = QHttpNetworkConnectionChannel::ClosingState;
1201
1202 // pendingEncrypt must only be true in between connected and encrypted states
1203 pendingEncrypt = false;
1204 }
1205}
1206
1207#ifndef QT_NO_NETWORKPROXY
1208void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1209{
1210 if ((connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1211 && (switchedToHttp2 || h2RequestsToSend.size() > 0))
1212 || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1213 if (h2RequestsToSend.size() > 0)
1214 connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1215 } else { // HTTP
1216 // Need to dequeue the request before we can emit the error.
1217 if (!reply)
1218 connection->d_func()->dequeueRequest(socket);
1219 if (reply)
1220 connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1221 }
1222}
1223#endif
1224
1230
1231void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
1232 const char *message)
1233{
1234 if (reply)
1235 emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1236 const auto h2RequestsToSendCopy = h2RequestsToSend;
1237 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1238 QHttpNetworkReply *currentReply = httpMessagePair.second;
1239 Q_ASSERT(currentReply);
1240 emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1241 }
1242}
1243
1244#ifndef QT_NO_SSL
1246{
1247 QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
1248 Q_ASSERT(sslSocket);
1249
1250 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 switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
1255 case QSslConfiguration::NextProtocolNegotiationNegotiated: {
1256 QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
1257 if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
1258 // fall through to create a QHttpProtocolHandler
1259 } else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
1260 switchedToHttp2 = true;
1261 protocolHandler.reset(new QHttp2ProtocolHandler(this));
1262 connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP2);
1263 break;
1264 } else {
1265 emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
1266 "detected unknown Next Protocol Negotiation protocol");
1267 break;
1268 }
1269 }
1270 Q_FALLTHROUGH();
1271 case QSslConfiguration::NextProtocolNegotiationUnsupported: // No agreement, try HTTP/1(.1)
1272 case QSslConfiguration::NextProtocolNegotiationNone: {
1273 protocolHandler.reset(new QHttpProtocolHandler(this));
1274
1275 QSslConfiguration newConfiguration = sslSocket->sslConfiguration();
1276 QList<QByteArray> protocols = newConfiguration.allowedNextProtocols();
1277 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 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
1281 protocols.removeAll(QSslConfiguration::ALPNProtocolHTTP2);
1282
1283 if (nProtocols > protocols.size()) {
1284 newConfiguration.setAllowedNextProtocols(protocols);
1285 const int channelCount = connection->d_func()->channelCount;
1286 for (int i = 0; i < channelCount; ++i)
1287 connection->d_func()->channels[i].setSslConfiguration(newConfiguration);
1288 }
1289
1290 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 if (connection->d_func()->activeChannelCount < connection->d_func()->channelCount) {
1295 connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
1296 // re-queue requests from HTTP/2 queue to HTTP queue, if any
1298 }
1299 break;
1300 }
1301 default:
1302 emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
1303 "detected unknown Next Protocol Negotiation protocol");
1304 }
1305 } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1306 || 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 protocolHandler.reset(new QHttp2ProtocolHandler(this));
1310 }
1311
1312 if (!socket)
1313 return; // ### error
1314 state = QHttpNetworkConnectionChannel::IdleState;
1315 pendingEncrypt = false;
1316
1317 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
1318 connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1319 if (!h2RequestsToSend.isEmpty()) {
1320 // Similar to HTTP/1.1 counterpart below:
1321 const auto &pair = std::as_const(h2RequestsToSend).first();
1323 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 QMetaObject::invokeMethod(this,
1331 &QHttpNetworkConnectionChannel::checkAndResumeCommunication,
1332 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 }
1337 } else { // HTTP
1338 if (!reply)
1339 connection->d_func()->dequeueRequest(socket);
1340 if (reply) {
1341 reply->setHttp2WasUsed(false);
1342 Q_ASSERT(reply->d_func()->connectionChannel == this);
1343 emit reply->encrypted();
1344 }
1345 if (reply)
1347 }
1348 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1349}
1350
1351
1353{
1354 Q_ASSERT(connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1355 || 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 if (!reply && h2RequestsToSend.isEmpty())
1363 abort();
1367 if (needInvokeReceiveReply)
1368 _q_receiveReply();
1371}
1372
1374{
1375 const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
1376 for (const auto &httpMessagePair : h2RequestsToSendCopy)
1377 connection->d_func()->requeueRequest(httpMessagePair);
1378}
1379
1380void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1381{
1382 if (!socket)
1383 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 connection->d_func()->pauseConnection();
1388 if (pendingEncrypt && !reply)
1389 connection->d_func()->dequeueRequest(socket);
1390 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
1391 if (reply)
1392 emit reply->sslErrors(errors);
1393 }
1394#ifndef QT_NO_SSL
1395 else { // HTTP/2
1396 const auto h2RequestsToSendCopy = h2RequestsToSend;
1397 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1398 // emit SSL errors for all replies
1399 QHttpNetworkReply *currentReply = httpMessagePair.second;
1400 Q_ASSERT(currentReply);
1401 emit currentReply->sslErrors(errors);
1402 }
1403 }
1404#endif // QT_NO_SSL
1405 connection->d_func()->resumeConnection();
1406}
1407
1408void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
1409{
1410 connection->d_func()->pauseConnection();
1411
1412 if (pendingEncrypt && !reply)
1413 connection->d_func()->dequeueRequest(socket);
1414
1415 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
1416 if (reply)
1417 emit reply->preSharedKeyAuthenticationRequired(authenticator);
1418 } else {
1419 const auto h2RequestsToSendCopy = h2RequestsToSend;
1420 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1421 // emit SSL errors for all replies
1422 QHttpNetworkReply *currentReply = httpMessagePair.second;
1423 Q_ASSERT(currentReply);
1424 emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
1425 }
1426 }
1427
1428 connection->d_func()->resumeConnection();
1429}
1430
1432{
1433 Q_UNUSED(bytes);
1434 // bytes have been written to the socket. write even more of them :)
1437 // otherwise we do nothing
1438}
1439
1440#endif
1441
1442void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1443{
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 connection = c;
1447}
1448
1449QT_END_NAMESPACE
1450
1451#include "moc_qhttpnetworkconnectionchannel_p.cpp"
void setProxy(const QNetworkProxy &networkProxy)
void _q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *)
void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message)
void _q_sslErrors(const QList< QSslError > &errors)
void _q_connected_abstract_socket(QAbstractSocket *socket)
void ignoreSslErrors(const QList< QSslError > &errors)
void _q_error(QAbstractSocket::SocketError)
void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth)
QHttp2ProtocolHandler * h2ProtocolHandler() const noexcept
The QNetworkProxy class provides a network layer proxy.
Combined button and popup list for selecting options.
static const char keepAliveCountOption[]
static const int TCP_KEEPIDLE_DEF
static const int TCP_KEEPINTVL_DEF
static QT_BEGIN_NAMESPACE const int reconnectAttemptsDefault
static const int TCP_KEEPCNT_DEF
static const char keepAliveIntervalOption[]
static const char keepAliveIdleOption[]
std::pair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair