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