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

source code of qtbase/src/network/access/qhttpnetworkconnectionchannel.cpp