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