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