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