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

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