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 
66 QT_BEGIN_NAMESPACE
67 
68 namespace
69 {
70 
71 class ProtocolHandlerDeleter : public QObject
72 {
73 public:
ProtocolHandlerDeleter(QAbstractProtocolHandler * h)74     explicit ProtocolHandlerDeleter(QAbstractProtocolHandler *h) : handler(h) {}
~ProtocolHandlerDeleter()75     ~ProtocolHandlerDeleter() { delete handler; }
76 private:
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:
87 static const int reconnectAttemptsDefault = 3;
88 
QHttpNetworkConnectionChannel()89 QHttpNetworkConnectionChannel::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 
init()117 void 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("_q_networksession", QVariant::fromValue(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(socket, SIGNAL(bytesWritten(qint64)),
141                      this, SLOT(_q_bytesWritten(qint64)),
142                      Qt::DirectConnection);
143     QObject::connect(socket, SIGNAL(connected()),
144                      this, SLOT(_q_connected()),
145                      Qt::DirectConnection);
146     QObject::connect(socket, SIGNAL(readyRead()),
147                      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(socket, SIGNAL(disconnected()),
158                      this, SLOT(_q_disconnected()),
159                      Qt::DirectConnection);
160     QObject::connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
161                      this, SLOT(_q_error(QAbstractSocket::SocketError)),
162                      Qt::DirectConnection);
163 
164 
165 #ifndef QT_NO_NETWORKPROXY
166     QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
167                      this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
168                      Qt::DirectConnection);
169 #endif
170 
171 #ifndef QT_NO_SSL
172     QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
173     if (sslSocket) {
174         // won't be a sslSocket if encrypt is false
175         QObject::connect(sslSocket, SIGNAL(encrypted()),
176                          this, SLOT(_q_encrypted()),
177                          Qt::DirectConnection);
178         QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
179                          this, SLOT(_q_sslErrors(QList<QSslError>)),
180                          Qt::DirectConnection);
181         QObject::connect(sslSocket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
182                          this, SLOT(_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
183                          Qt::DirectConnection);
184         QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
185                          this, SLOT(_q_encryptedBytesWritten(qint64)),
186                          Qt::DirectConnection);
187 
188         if (ignoreAllSslErrors)
189             sslSocket->ignoreSslErrors();
190 
191         if (!ignoreSslErrorsList.isEmpty())
192             sslSocket->ignoreSslErrors(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(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 
close()212 void 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 
abort()235 void 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 
sendRequest()255 bool 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  */
sendRequestDelayed()267 void QHttpNetworkConnectionChannel::sendRequestDelayed()
268 {
269     QMetaObject::invokeMethod(this, [this] {
270         Q_ASSERT(!protocolHandler.isNull());
271         if (reply)
272             protocolHandler->sendRequest();
273     }, Qt::ConnectionType::QueuedConnection);
274 }
275 
_q_receiveReply()276 void QHttpNetworkConnectionChannel::_q_receiveReply()
277 {
278     Q_ASSERT(!protocolHandler.isNull());
279     protocolHandler->_q_receiveReply();
280 }
281 
_q_readyRead()282 void 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
handleUnexpectedEOF()289 void 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(QNetworkReply::RemoteHostClosedError, socket);
297         emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
298         reply = nullptr;
299         if (protocolHandler)
300             protocolHandler->setReply(nullptr);
301         request = QHttpNetworkRequest();
302         QMetaObject::invokeMethod(connection, "_q_startNextRequest", 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 
ensureConnection()312 bool 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(authenticator);
350         priv->hasFailed = false;
351         proxyAuthenticator.detach();
352         priv = QAuthenticatorPrivate::getPrivate(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(authenticator);
362         if (priv && priv->phase == QAuthenticatorPrivate::Done)
363             priv->phase = QAuthenticatorPrivate::Start;
364         priv = QAuthenticatorPrivate::getPrivate(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("user-agent");
388                 } else {
389                     value = connection->d_func()->predictNextRequest().headerField("user-agent");
390                 }
391             } else {
392                 value = request.headerField("user-agent");
393             }
394             if (!value.isEmpty()) {
395                 QNetworkProxy proxy(socket->proxy());
396                 proxy.setRawHeader("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*>(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(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
413             if (ignoreAllSslErrors)
414                 sslSocket->ignoreSslErrors();
415             sslSocket->ignoreSslErrors(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(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, 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(connectHost, connectPort, QIODevice::ReadWrite, 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 
allDone()461 void QHttpNetworkConnectionChannel::allDone()
462 {
463     Q_ASSERT(reply);
464 
465     if (!reply) {
466         qWarning("QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.io/");
467         return;
468     }
469 
470     // For clear text HTTP/2 we tried to upgrade from HTTP/1.1 to HTTP/2; for
471     // ConnectionTypeHTTP2Direct we can never be here in case of failure
472     // (after an attempt to read HTTP/1.1 as HTTP/2 frames) or we have a normal
473     // HTTP/2 response and thus can skip this test:
474     if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
475         && !ssl && !switchedToHttp2) {
476         if (Http2::is_protocol_upgraded(*reply)) {
477             switchedToHttp2 = true;
478             protocolHandler->setReply(nullptr);
479 
480             // As allDone() gets called from the protocol handler, it's not yet
481             // safe to delete it. There is no 'deleteLater', since
482             // QAbstractProtocolHandler is not a QObject. Instead 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(new QHttp2ProtocolHandler(this));
497             QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
498             QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection);
499             QMetaObject::invokeMethod(connection, "_q_startNextRequest", 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(h2c, "ensureClientPrefaceSent", 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(reply, "finished", 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(connection, "_q_startNextRequest", Qt::QueuedConnection);
576     } else if (alreadyPipelinedRequests.isEmpty()) {
577         if (connectionCloseEnabled)
578             if (socket->state() != QAbstractSocket::UnconnectedState)
579                 close();
580         if (qobject_cast<QHttpNetworkConnection*>(connection))
581             QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
582     }
583 }
584 
detectPipeliningSupport()585 void 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("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
600             && (!serverHeaderField.contains("Microsoft-IIS/5."))
601             && (!serverHeaderField.contains("Netscape-Enterprise/3."))
602             // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
603             && (!serverHeaderField.contains("WebLogic"))
604             && (!serverHeaderField.startsWith("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
requeueCurrentlyPipelinedRequests()613 void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
614 {
615     for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
616         connection->d_func()->requeueRequest(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*>(connection))
623         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
624 }
625 
handleStatus()626 void 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 *>(connection)) {
652             QMetaObject::invokeMethod(connection, "_q_startNextRequest", 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, (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(connection, "_q_startNextRequest", Qt::QueuedConnection);
669                 } else {
670                     // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
671                     closeAndResendCurrentRequest();
672                     QMetaObject::invokeMethod(connection, "_q_startNextRequest", 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, reply->d_func()->errorString);
686         }
687         break;
688     default:
689         if (qobject_cast<QHttpNetworkConnection*>(connection))
690             QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
691     }
692 }
693 
resetUploadData()694 bool QHttpNetworkConnectionChannel::resetUploadData()
695 {
696     if (!reply) {
697         //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
698         return false;
699     }
700     QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
701     if (!uploadByteDevice)
702         return true;
703 
704     if (uploadByteDevice->reset()) {
705         written = 0;
706         return true;
707     } else {
708         connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
709         return false;
710     }
711 }
712 
713 #ifndef QT_NO_NETWORKPROXY
714 
setProxy(const QNetworkProxy & networkProxy)715 void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
716 {
717     if (socket)
718         socket->setProxy(networkProxy);
719 
720     proxy = networkProxy;
721 }
722 
723 #endif
724 
725 #ifndef QT_NO_SSL
726 
ignoreSslErrors()727 void QHttpNetworkConnectionChannel::ignoreSslErrors()
728 {
729     if (socket)
730         static_cast<QSslSocket *>(socket)->ignoreSslErrors();
731 
732     ignoreAllSslErrors = true;
733 }
734 
735 
ignoreSslErrors(const QList<QSslError> & errors)736 void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors)
737 {
738     if (socket)
739         static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors);
740 
741     ignoreSslErrorsList = errors;
742 }
743 
setSslConfiguration(const QSslConfiguration & config)744 void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config)
745 {
746     if (socket)
747         static_cast<QSslSocket *>(socket)->setSslConfiguration(config);
748 
749     if (sslConfiguration.data())
750         *sslConfiguration = config;
751     else
752         sslConfiguration.reset(new QSslConfiguration(config));
753 }
754 
755 #endif
756 
pipelineInto(HttpMessagePair & pair)757 void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
758 {
759     // this is only called for simple GET
760 
761     QHttpNetworkRequest &request = pair.first;
762     QHttpNetworkReply *reply = pair.second;
763     reply->d_func()->clear();
764     reply->d_func()->connection = connection;
765     reply->d_func()->connectionChannel = this;
766     reply->d_func()->autoDecompress = request.d->autoDecompress;
767     reply->d_func()->pipeliningUsed = true;
768 
769 #ifndef QT_NO_NETWORKPROXY
770     pipeline.append(QHttpNetworkRequestPrivate::header(request,
771                                                            (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
772 #else
773     pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
774 #endif
775 
776     alreadyPipelinedRequests.append(pair);
777 
778     // pipelineFlush() needs to be called at some point afterwards
779 }
780 
pipelineFlush()781 void QHttpNetworkConnectionChannel::pipelineFlush()
782 {
783     if (pipeline.isEmpty())
784         return;
785 
786     // The goal of this is so that we have everything in one TCP packet.
787     // For the Unbuffered QTcpSocket this is manually needed, the buffered
788     // QTcpSocket does it automatically.
789     // Also, sometimes the OS does it for us (Nagle's algorithm) but that
790     // happens only sometimes.
791     socket->write(pipeline);
792     pipeline.clear();
793 }
794 
795 
closeAndResendCurrentRequest()796 void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
797 {
798     requeueCurrentlyPipelinedRequests();
799     close();
800     if (reply)
801         resendCurrent = true;
802     if (qobject_cast<QHttpNetworkConnection*>(connection))
803         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
804 }
805 
resendCurrentRequest()806 void QHttpNetworkConnectionChannel::resendCurrentRequest()
807 {
808     requeueCurrentlyPipelinedRequests();
809     if (reply)
810         resendCurrent = true;
811     if (qobject_cast<QHttpNetworkConnection*>(connection))
812         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
813 }
814 
isSocketBusy() const815 bool QHttpNetworkConnectionChannel::isSocketBusy() const
816 {
817     return (state & QHttpNetworkConnectionChannel::BusyState);
818 }
819 
isSocketWriting() const820 bool QHttpNetworkConnectionChannel::isSocketWriting() const
821 {
822     return (state & QHttpNetworkConnectionChannel::WritingState);
823 }
824 
isSocketWaiting() const825 bool QHttpNetworkConnectionChannel::isSocketWaiting() const
826 {
827     return (state & QHttpNetworkConnectionChannel::WaitingState);
828 }
829 
isSocketReading() const830 bool QHttpNetworkConnectionChannel::isSocketReading() const
831 {
832     return (state & QHttpNetworkConnectionChannel::ReadingState);
833 }
834 
_q_bytesWritten(qint64 bytes)835 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
836 {
837     Q_UNUSED(bytes);
838     if (ssl) {
839         // In the SSL case we want to send data from encryptedBytesWritten signal since that one
840         // is the one going down to the actual network, not only into some SSL buffer.
841         return;
842     }
843 
844     // bytes have been written to the socket. write even more of them :)
845     if (isSocketWriting())
846         sendRequest();
847     // otherwise we do nothing
848 }
849 
_q_disconnected()850 void QHttpNetworkConnectionChannel::_q_disconnected()
851 {
852     if (state == QHttpNetworkConnectionChannel::ClosingState) {
853         state = QHttpNetworkConnectionChannel::IdleState;
854         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
855         return;
856     }
857 
858     // read the available data before closing (also done in _q_error for other codepaths)
859     if ((isSocketWaiting() || isSocketReading()) && socket->bytesAvailable()) {
860         if (reply) {
861             state = QHttpNetworkConnectionChannel::ReadingState;
862             _q_receiveReply();
863         }
864     } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
865         // re-sending request because the socket was in ClosingState
866         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
867     }
868     state = QHttpNetworkConnectionChannel::IdleState;
869     if (alreadyPipelinedRequests.length()) {
870         // If nothing was in a pipeline, no need in calling
871         // _q_startNextRequest (which it does):
872         requeueCurrentlyPipelinedRequests();
873     }
874 
875     pendingEncrypt = false;
876 }
877 
878 
_q_connected()879 void QHttpNetworkConnectionChannel::_q_connected()
880 {
881     // For the Happy Eyeballs we need to check if this is the first channel to connect.
882     if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::HostLookupPending || connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4or6) {
883         if (connection->d_func()->delayedConnectionTimer.isActive())
884             connection->d_func()->delayedConnectionTimer.stop();
885         if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
886             connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
887         else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
888             connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
889         else {
890             if (socket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
891                 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
892             else
893                 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
894         }
895         connection->d_func()->networkLayerDetected(networkLayerPreference);
896     } else {
897         bool anyProtocol = networkLayerPreference == QAbstractSocket::AnyIPProtocol;
898         if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4)
899              && (networkLayerPreference != QAbstractSocket::IPv4Protocol && !anyProtocol))
900             || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6)
901                 && (networkLayerPreference != QAbstractSocket::IPv6Protocol && !anyProtocol))) {
902             close();
903             // This is the second connection so it has to be closed and we can schedule it for another request.
904             QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
905             return;
906         }
907         //The connections networkLayerState had already been decided.
908     }
909 
910     // improve performance since we get the request sent by the kernel ASAP
911     //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
912     // We have this commented out now. It did not have the effect we wanted. If we want to
913     // do this properly, Qt has to combine multiple HTTP requests into one buffer
914     // and send this to the kernel in one syscall and then the kernel immediately sends
915     // it as one TCP packet because of TCP_NODELAY.
916     // However, this code is currently not in Qt, so we rely on the kernel combining
917     // the requests into one TCP packet.
918 
919     // not sure yet if it helps, but it makes sense
920     socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
921 
922     pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
923 
924     if (QNetworkStatusMonitor::isEnabled()) {
925         auto connectionPrivate = connection->d_func();
926         if (!connectionPrivate->connectionMonitor.isMonitoring()) {
927             // Now that we have a pair of addresses, we can start monitoring the
928             // connection status to handle its loss properly.
929             if (connectionPrivate->connectionMonitor.setTargets(socket->localAddress(), socket->peerAddress()))
930                 connectionPrivate->connectionMonitor.startMonitoring();
931         }
932     }
933 
934     // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
935     //channels[i].reconnectAttempts = 2;
936     if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
937 #ifndef QT_NO_SSL
938         if (connection->sslContext().isNull()) {
939             // this socket is making the 1st handshake for this connection,
940             // we need to set the SSL context so new sockets can reuse it
941             QSharedPointer<QSslContext> socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(socket));
942             if (!socketSslContext.isNull())
943                 connection->setSslContext(socketSslContext);
944         }
945 #endif
946     } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
947         state = QHttpNetworkConnectionChannel::IdleState;
948         protocolHandler.reset(new QHttp2ProtocolHandler(this));
949         if (spdyRequestsToSend.count() > 0) {
950             // In case our peer has sent us its settings (window size, max concurrent streams etc.)
951             // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
952             QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
953         }
954     } else {
955         state = QHttpNetworkConnectionChannel::IdleState;
956         const bool tryProtocolUpgrade = connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2;
957         if (tryProtocolUpgrade) {
958             // For HTTP/1.1 it's already created and never reset.
959             protocolHandler.reset(new QHttpProtocolHandler(this));
960         }
961         switchedToHttp2 = false;
962 
963         if (!reply)
964             connection->d_func()->dequeueRequest(socket);
965 
966         if (reply) {
967             if (tryProtocolUpgrade) {
968                 // Let's augment our request with some magic headers and try to
969                 // switch to HTTP/2.
970                 Http2::appendProtocolUpgradeHeaders(connection->http2Parameters(), &request);
971             }
972             sendRequest();
973         }
974     }
975 }
976 
977 
_q_error(QAbstractSocket::SocketError socketError)978 void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
979 {
980     if (!socket)
981         return;
982     QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
983 
984     switch (socketError) {
985     case QAbstractSocket::HostNotFoundError:
986         errorCode = QNetworkReply::HostNotFoundError;
987         break;
988     case QAbstractSocket::ConnectionRefusedError:
989         errorCode = QNetworkReply::ConnectionRefusedError;
990         break;
991     case QAbstractSocket::RemoteHostClosedError:
992         // This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
993         // Depending on timing it can also come three times in a row (first time when we try to write into a closing QSslSocket).
994         // The reconnectAttempts handling catches the cases where we can re-send the request.
995         if (!reply && state == QHttpNetworkConnectionChannel::IdleState) {
996             // Not actually an error, it is normal for Keep-Alive connections to close after some time if no request
997             // is sent on them. No need to error the other replies below. Just bail out here.
998             // The _q_disconnected will handle the possibly pipelined replies. HTTP/2 is special for now,
999             // we do not resend, but must report errors if any request is in progress (note, while
1000             // not in its sendRequest(), protocol handler switches the channel to IdleState, thus
1001             // this check is under this condition in 'if'):
1002             if (protocolHandler.data()) {
1003                 if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
1004                     || (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1005                         && switchedToHttp2)) {
1006                     auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
1007                     h2Handler->handleConnectionClosure();
1008                 }
1009             }
1010             return;
1011         } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
1012             // Try to reconnect/resend before sending an error.
1013             // While "Reading" the _q_disconnected() will handle this.
1014             // If we're using ssl then the protocolHandler is not initialized until
1015             // "encrypted" has been emitted, since retrying requires the protocolHandler (asserted)
1016             // we will not try if encryption is not done.
1017             if (!pendingEncrypt && reconnectAttempts-- > 0) {
1018                 resendCurrentRequest();
1019                 return;
1020             } else {
1021                 errorCode = QNetworkReply::RemoteHostClosedError;
1022             }
1023         } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1024             if (!reply)
1025                 break;
1026 
1027             if (!reply->d_func()->expectContent()) {
1028                 // No content expected, this is a valid way to have the connection closed by the server
1029                 // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
1030                 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1031                 return;
1032             }
1033             if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1034                 // There was no content-length header and it's not chunked encoding,
1035                 // so this is a valid way to have the connection closed by the server
1036                 // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
1037                 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1038                 return;
1039             }
1040             // ok, we got a disconnect even though we did not expect it
1041             // Try to read everything from the socket before we emit the error.
1042             if (socket->bytesAvailable()) {
1043                 // Read everything from the socket into the reply buffer.
1044                 // we can ignore the readbuffersize as the data is already
1045                 // in memory and we will not receive more data on the socket.
1046                 reply->setReadBufferSize(0);
1047                 reply->setDownstreamLimited(false);
1048                 _q_receiveReply();
1049                 if (!reply) {
1050                     // No more reply assigned after the previous call? Then it had been finished successfully.
1051                     requeueCurrentlyPipelinedRequests();
1052                     state = QHttpNetworkConnectionChannel::IdleState;
1053                     QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1054                     return;
1055                 }
1056             }
1057 
1058             errorCode = QNetworkReply::RemoteHostClosedError;
1059         } else {
1060             errorCode = QNetworkReply::RemoteHostClosedError;
1061         }
1062         break;
1063     case QAbstractSocket::SocketTimeoutError:
1064         // try to reconnect/resend before sending an error.
1065         if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1066             resendCurrentRequest();
1067             return;
1068         }
1069         errorCode = QNetworkReply::TimeoutError;
1070         break;
1071     case QAbstractSocket::ProxyAuthenticationRequiredError:
1072         errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1073         break;
1074     case QAbstractSocket::SslHandshakeFailedError:
1075         errorCode = QNetworkReply::SslHandshakeFailedError;
1076         break;
1077     case QAbstractSocket::ProxyConnectionClosedError:
1078         // try to reconnect/resend before sending an error.
1079         if (reconnectAttempts-- > 0) {
1080             resendCurrentRequest();
1081             return;
1082         }
1083         errorCode = QNetworkReply::ProxyConnectionClosedError;
1084         break;
1085     case QAbstractSocket::ProxyConnectionTimeoutError:
1086         // try to reconnect/resend before sending an error.
1087         if (reconnectAttempts-- > 0) {
1088             resendCurrentRequest();
1089             return;
1090         }
1091         errorCode = QNetworkReply::ProxyTimeoutError;
1092         break;
1093     default:
1094         // all other errors are treated as NetworkError
1095         errorCode = QNetworkReply::UnknownNetworkError;
1096         break;
1097     }
1098     QPointer<QHttpNetworkConnection> that = connection;
1099     QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1100 
1101     // In the HostLookupPending state the channel should not emit the error.
1102     // This will instead be handled by the connection.
1103     if (!connection->d_func()->shouldEmitChannelError(socket))
1104         return;
1105 
1106     // emit error for all waiting replies
1107     do {
1108         // First requeue the already pipelined requests for the current failed reply,
1109         // then dequeue pending requests so we can also mark them as finished with error
1110         if (reply)
1111             requeueCurrentlyPipelinedRequests();
1112         else
1113             connection->d_func()->dequeueRequest(socket);
1114 
1115         if (reply) {
1116             reply->d_func()->errorString = errorString;
1117             reply->d_func()->httpErrorCode = errorCode;
1118             emit reply->finishedWithError(errorCode, errorString);
1119             reply = nullptr;
1120             if (protocolHandler)
1121                 protocolHandler->setReply(nullptr);
1122         }
1123     } while (!connection->d_func()->highPriorityQueue.isEmpty()
1124              || !connection->d_func()->lowPriorityQueue.isEmpty());
1125 
1126     if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1127         || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
1128 #ifndef QT_NO_SSL
1129         || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
1130 #endif
1131        ) {
1132         QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
1133         for (int a = 0; a < spdyPairs.count(); ++a) {
1134             // emit error for all replies
1135             QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
1136             currentReply->d_func()->errorString = errorString;
1137             currentReply->d_func()->httpErrorCode = errorCode;
1138             Q_ASSERT(currentReply);
1139             emit currentReply->finishedWithError(errorCode, errorString);
1140         }
1141         spdyRequestsToSend.clear();
1142     }
1143 
1144     // send the next request
1145     QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1146 
1147     if (that) {
1148         //signal emission triggered event loop
1149         if (!socket)
1150             state = QHttpNetworkConnectionChannel::IdleState;
1151         else if (socket->state() == QAbstractSocket::UnconnectedState)
1152             state = QHttpNetworkConnectionChannel::IdleState;
1153         else
1154             state = QHttpNetworkConnectionChannel::ClosingState;
1155 
1156         // pendingEncrypt must only be true in between connected and encrypted states
1157         pendingEncrypt = false;
1158     }
1159 }
1160 
1161 #ifndef QT_NO_NETWORKPROXY
_q_proxyAuthenticationRequired(const QNetworkProxy & proxy,QAuthenticator * auth)1162 void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1163 {
1164     if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1165         || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
1166 #ifndef QT_NO_SSL
1167         || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
1168 #endif
1169         ) {
1170         if (spdyRequestsToSend.count() > 0)
1171             connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1172     } else { // HTTP
1173         // Need to dequeue the request before we can emit the error.
1174         if (!reply)
1175             connection->d_func()->dequeueRequest(socket);
1176         if (reply)
1177             connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1178     }
1179 }
1180 #endif
1181 
_q_uploadDataReadyRead()1182 void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
1183 {
1184     if (reply)
1185         sendRequest();
1186 }
1187 
emitFinishedWithError(QNetworkReply::NetworkError error,const char * message)1188 void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
1189                                                           const char *message)
1190 {
1191     if (reply)
1192         emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1193     QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
1194     for (int a = 0; a < spdyPairs.count(); ++a) {
1195         QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
1196         Q_ASSERT(currentReply);
1197         emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1198     }
1199 }
1200 
1201 #ifndef QT_NO_SSL
_q_encrypted()1202 void QHttpNetworkConnectionChannel::_q_encrypted()
1203 {
1204     QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
1205     Q_ASSERT(sslSocket);
1206 
1207     if (!protocolHandler && connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1208         // ConnectionTypeHTTP2Direct does not rely on ALPN/NPN to negotiate HTTP/2,
1209         // after establishing a secure connection we immediately start sending
1210         // HTTP/2 frames.
1211         switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
1212         case QSslConfiguration::NextProtocolNegotiationNegotiated: {
1213             QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
1214             if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
1215                 // fall through to create a QHttpProtocolHandler
1216             } else if (nextProtocol == QSslConfiguration::NextProtocolSpdy3_0) {
1217                 protocolHandler.reset(new QSpdyProtocolHandler(this));
1218                 connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeSPDY);
1219                 // no need to re-queue requests, if SPDY was enabled on the request it
1220                 // has gone to the SPDY queue already
1221                 break;
1222             } else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
1223                 switchedToHttp2 = true;
1224                 protocolHandler.reset(new QHttp2ProtocolHandler(this));
1225                 connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP2);
1226                 break;
1227             } else {
1228                 emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
1229                                       "detected unknown Next Protocol Negotiation protocol");
1230                 break;
1231             }
1232         }
1233             Q_FALLTHROUGH();
1234         case QSslConfiguration::NextProtocolNegotiationUnsupported: // No agreement, try HTTP/1(.1)
1235         case QSslConfiguration::NextProtocolNegotiationNone: {
1236             protocolHandler.reset(new QHttpProtocolHandler(this));
1237 
1238             QSslConfiguration newConfiguration = sslSocket->sslConfiguration();
1239             QList<QByteArray> protocols = newConfiguration.allowedNextProtocols();
1240             const int nProtocols = protocols.size();
1241             // Clear the protocol that we failed to negotiate, so we do not try
1242             // it again on other channels that our connection can create/open.
1243             if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
1244                 protocols.removeAll(QSslConfiguration::ALPNProtocolHTTP2);
1245             else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY)
1246                 protocols.removeAll(QSslConfiguration::NextProtocolSpdy3_0);
1247 
1248             if (nProtocols > protocols.size()) {
1249                 newConfiguration.setAllowedNextProtocols(protocols);
1250                 const int channelCount = connection->d_func()->channelCount;
1251                 for (int i = 0; i < channelCount; ++i)
1252                     connection->d_func()->channels[i].setSslConfiguration(newConfiguration);
1253             }
1254 
1255             connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
1256             // We use only one channel for SPDY or HTTP/2, but normally six for
1257             // HTTP/1.1 - let's restore this number to the reserved number of
1258             // channels:
1259             if (connection->d_func()->activeChannelCount < connection->d_func()->channelCount) {
1260                 connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
1261                 // re-queue requests from SPDY queue to HTTP queue, if any
1262                 requeueSpdyRequests();
1263             }
1264             break;
1265         }
1266         default:
1267             emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
1268                                   "detected unknown Next Protocol Negotiation protocol");
1269         }
1270     } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1271                || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1272         // We have to reset QHttp2ProtocolHandler's state machine, it's a new
1273         // connection and the handler's state is unique per connection.
1274         protocolHandler.reset(new QHttp2ProtocolHandler(this));
1275     }
1276 
1277     if (!socket)
1278         return; // ### error
1279     state = QHttpNetworkConnectionChannel::IdleState;
1280     pendingEncrypt = false;
1281 
1282     if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
1283         connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
1284         connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1285         // we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent
1286         if (spdyRequestsToSend.count() > 0) {
1287             // In case our peer has sent us its settings (window size, max concurrent streams etc.)
1288             // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
1289             QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1290         }
1291     } else { // HTTP
1292         if (!reply)
1293             connection->d_func()->dequeueRequest(socket);
1294         if (reply) {
1295             reply->setSpdyWasUsed(false);
1296             Q_ASSERT(reply->d_func()->connectionChannel == this);
1297             emit reply->encrypted();
1298         }
1299         if (reply)
1300             sendRequestDelayed();
1301     }
1302 }
1303 
requeueSpdyRequests()1304 void QHttpNetworkConnectionChannel::requeueSpdyRequests()
1305 {
1306     QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
1307     for (int a = 0; a < spdyPairs.count(); ++a) {
1308         connection->d_func()->requeueRequest(spdyPairs.at(a));
1309     }
1310     spdyRequestsToSend.clear();
1311 }
1312 
_q_sslErrors(const QList<QSslError> & errors)1313 void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1314 {
1315     if (!socket)
1316         return;
1317     //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1318     // Also pause the connection because socket notifiers may fire while an user
1319     // dialog is displaying
1320     connection->d_func()->pauseConnection();
1321     if (pendingEncrypt && !reply)
1322         connection->d_func()->dequeueRequest(socket);
1323     if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
1324         if (reply)
1325             emit reply->sslErrors(errors);
1326     }
1327 #ifndef QT_NO_SSL
1328     else { // SPDY
1329         QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
1330         for (int a = 0; a < spdyPairs.count(); ++a) {
1331             // emit SSL errors for all replies
1332             QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
1333             Q_ASSERT(currentReply);
1334             emit currentReply->sslErrors(errors);
1335         }
1336     }
1337 #endif // QT_NO_SSL
1338     connection->d_func()->resumeConnection();
1339 }
1340 
_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator * authenticator)1341 void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
1342 {
1343     connection->d_func()->pauseConnection();
1344 
1345     if (pendingEncrypt && !reply)
1346         connection->d_func()->dequeueRequest(socket);
1347 
1348     if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
1349         if (reply)
1350             emit reply->preSharedKeyAuthenticationRequired(authenticator);
1351     } else {
1352         QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
1353         for (int a = 0; a < spdyPairs.count(); ++a) {
1354             // emit SSL errors for all replies
1355             QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
1356             Q_ASSERT(currentReply);
1357             emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
1358         }
1359     }
1360 
1361     connection->d_func()->resumeConnection();
1362 }
1363 
_q_encryptedBytesWritten(qint64 bytes)1364 void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
1365 {
1366     Q_UNUSED(bytes);
1367     // bytes have been written to the socket. write even more of them :)
1368     if (isSocketWriting())
1369         sendRequest();
1370     // otherwise we do nothing
1371 }
1372 
1373 #endif
1374 
setConnection(QHttpNetworkConnection * c)1375 void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1376 {
1377     // Inlining this function in the header leads to compiler error on
1378     // release-armv5, on at least timebox 9.2 and 10.1.
1379     connection = c;
1380 }
1381 
1382 QT_END_NAMESPACE
1383 
1384 #include "moc_qhttpnetworkconnectionchannel_p.cpp"
1385