1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qhttpnetworkconnection_p.h"
41#include <private/qabstractsocket_p.h>
42#include "qhttpnetworkconnectionchannel_p.h"
43#include "private/qnoncontiguousbytedevice_p.h"
44#include <private/qnetworkrequest_p.h>
45#include <private/qobject_p.h>
46#include <private/qauthenticator_p.h>
47#include "private/qhostinfo_p.h"
48#include <qnetworkproxy.h>
49#include <qauthenticator.h>
50#include <qcoreapplication.h>
51
52#include <qbuffer.h>
53#include <qpair.h>
54#include <qdebug.h>
55
56#ifndef QT_NO_SSL
57# include <private/qsslsocket_p.h>
58# include <QtNetwork/qsslkey.h>
59# include <QtNetwork/qsslcipher.h>
60# include <QtNetwork/qsslconfiguration.h>
61# include <QtNetwork/qsslerror.h>
62#endif
63
64
65
66QT_BEGIN_NAMESPACE
67
68const int QHttpNetworkConnectionPrivate::defaultHttpChannelCount = 6;
69
70// The pipeline length. So there will be 4 requests in flight.
71const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
72// Only re-fill the pipeline if there's defaultRePipelineLength slots free in the pipeline.
73// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
74const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
75
76
77QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName,
78 quint16 port, bool encrypt,
79 QHttpNetworkConnection::ConnectionType type)
80: state(RunningState),
81 networkLayerState(Unknown),
82 hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
83 , activeChannelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2
84 || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
85 ? 1 : defaultHttpChannelCount)
86 , channelCount(defaultHttpChannelCount)
87#ifndef QT_NO_NETWORKPROXY
88 , networkProxy(QNetworkProxy::NoProxy)
89#endif
90 , preConnectRequests(0)
91 , connectionType(type)
92{
93 // We allocate all 6 channels even if it's HTTP/2 enabled connection:
94 // in case the protocol negotiation via NPN/ALPN fails, we will have
95 // normally working HTTP/1.1.
96 Q_ASSERT(channelCount >= activeChannelCount);
97 channels = new QHttpNetworkConnectionChannel[channelCount];
98}
99
100QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName,
101 quint16 port, bool encrypt,
102 QHttpNetworkConnection::ConnectionType type)
103: state(RunningState), networkLayerState(Unknown),
104 hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
105 activeChannelCount(connectionCount), channelCount(connectionCount)
106#ifndef QT_NO_NETWORKPROXY
107 , networkProxy(QNetworkProxy::NoProxy)
108#endif
109 , preConnectRequests(0)
110 , connectionType(type)
111{
112 channels = new QHttpNetworkConnectionChannel[channelCount];
113}
114
115
116
117QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
118{
119 for (int i = 0; i < channelCount; ++i) {
120 if (channels[i].socket) {
121 QObject::disconnect(channels[i].socket, nullptr, &channels[i], nullptr);
122 channels[i].socket->close();
123 delete channels[i].socket;
124 }
125 }
126 delete []channels;
127}
128
129void QHttpNetworkConnectionPrivate::init()
130{
131 Q_Q(QHttpNetworkConnection);
132 for (int i = 0; i < channelCount; i++) {
133 channels[i].setConnection(this->q_func());
134 channels[i].ssl = encrypt;
135 }
136
137 delayedConnectionTimer.setSingleShot(true);
138 QObject::connect(&delayedConnectionTimer, SIGNAL(timeout()), q, SLOT(_q_connectDelayedChannel()));
139}
140
141void QHttpNetworkConnectionPrivate::pauseConnection()
142{
143 state = PausedState;
144
145 // Disable all socket notifiers
146 for (int i = 0; i < activeChannelCount; i++) {
147 if (channels[i].socket) {
148#ifndef QT_NO_SSL
149 if (encrypt)
150 QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
151 else
152#endif
153 QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
154 }
155 }
156}
157
158void QHttpNetworkConnectionPrivate::resumeConnection()
159{
160 state = RunningState;
161 // Enable all socket notifiers
162 for (int i = 0; i < activeChannelCount; i++) {
163 if (channels[i].socket) {
164#ifndef QT_NO_SSL
165 if (encrypt)
166 QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
167 else
168#endif
169 QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
170
171 // Resume pending upload if needed
172 if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
173 QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
174 }
175 }
176
177 // queue _q_startNextRequest
178 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
179}
180
181int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
182{
183 for (int i = 0; i < activeChannelCount; ++i)
184 if (channels[i].socket == socket)
185 return i;
186
187 qFatal("Called with unknown socket object.");
188 return 0;
189}
190
191// If the connection is in the HostLookupPendening state channel errors should not always be
192// emitted. This function will check the status of the connection channels if we
193// have not decided the networkLayerState and will return true if the channel error
194// should be emitted by the channel.
195bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *socket)
196{
197 Q_Q(QHttpNetworkConnection);
198
199 bool emitError = true;
200 int i = indexOf(socket);
201 int otherSocket = (i == 0 ? 1 : 0);
202
203 // If the IPv4 connection still isn't started we need to start it now.
204 if (delayedConnectionTimer.isActive()) {
205 delayedConnectionTimer.stop();
206 channels[otherSocket].ensureConnection();
207 }
208
209 if (activeChannelCount < channelCount) {
210 if (networkLayerState == HostLookupPending || networkLayerState == IPv4or6)
211 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
212 channels[0].close();
213 emitError = true;
214 } else {
215 if (networkLayerState == HostLookupPending || networkLayerState == IPv4or6) {
216 if (channels[otherSocket].isSocketBusy() && (channels[otherSocket].state != QHttpNetworkConnectionChannel::ClosingState)) {
217 // this was the first socket to fail.
218 channels[i].close();
219 emitError = false;
220 }
221 else {
222 // Both connection attempts has failed.
223 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
224 channels[i].close();
225 emitError = true;
226 }
227 } else {
228 if (((networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (channels[i].networkLayerPreference != QAbstractSocket::IPv4Protocol))
229 || ((networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (channels[i].networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
230 // First connection worked so this is the second one to complete and it failed.
231 channels[i].close();
232 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
233 emitError = false;
234 }
235 if (networkLayerState == QHttpNetworkConnectionPrivate::Unknown)
236 qWarning("We got a connection error when networkLayerState is Unknown");
237 }
238 }
239 return emitError;
240}
241
242
243qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
244{
245 return reply.d_func()->responseData.byteAmount();
246}
247
248qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
249{
250 return reply.d_func()->responseData.sizeNextBlock();
251}
252
253void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
254{
255 QHttpNetworkRequest &request = messagePair.first;
256 QHttpNetworkReply *reply = messagePair.second;
257
258 // add missing fields for the request
259 QByteArray value;
260 // check if Content-Length is provided
261 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
262#ifndef Q_OS_WASM
263 if (uploadByteDevice) {
264 const qint64 contentLength = request.contentLength();
265 const qint64 uploadDeviceSize = uploadByteDevice->size();
266 if (contentLength != -1 && uploadDeviceSize != -1) {
267 // both values known, take the smaller one.
268 request.setContentLength(qMin(uploadDeviceSize, contentLength));
269 } else if (contentLength == -1 && uploadDeviceSize != -1) {
270 // content length not supplied by user, but the upload device knows it
271 request.setContentLength(uploadDeviceSize);
272 } else if (contentLength != -1 && uploadDeviceSize == -1) {
273 // everything OK, the user supplied us the contentLength
274 } else if (Q_UNLIKELY(contentLength == -1 && uploadDeviceSize == -1)) {
275 qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
276 }
277 }
278#endif
279 // set the Connection/Proxy-Connection: Keep-Alive headers
280#ifndef QT_NO_NETWORKPROXY
281 if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) {
282 value = request.headerField("proxy-connection");
283 if (value.isEmpty())
284 request.setHeaderField("Proxy-Connection", "Keep-Alive");
285 } else {
286#endif
287 value = request.headerField("connection");
288 if (value.isEmpty())
289 request.setHeaderField("Connection", "Keep-Alive");
290#ifndef QT_NO_NETWORKPROXY
291 }
292#endif
293
294 // If the request had a accept-encoding set, we better not mess
295 // with it. If it was not set, we announce that we understand gzip
296 // and remember this fact in request.d->autoDecompress so that
297 // we can later decompress the HTTP reply if it has such an
298 // encoding.
299 value = request.headerField("accept-encoding");
300 if (value.isEmpty()) {
301#ifndef QT_NO_COMPRESS
302 const QByteArrayList &acceptedEncoding = QDecompressHelper::acceptedEncoding();
303 request.setHeaderField("Accept-Encoding", acceptedEncoding.join(", "));
304 request.d->autoDecompress = true;
305#else
306 // if zlib is not available set this to false always
307 request.d->autoDecompress = false;
308#endif
309 }
310
311 // some websites mandate an accept-language header and fail
312 // if it is not sent. This is a problem with the website and
313 // not with us, but we work around this by setting
314 // one always.
315 value = request.headerField("accept-language");
316 if (value.isEmpty()) {
317 QString systemLocale = QLocale::system().name().replace(QChar::fromLatin1('_'),QChar::fromLatin1('-'));
318 QString acceptLanguage;
319 if (systemLocale == QLatin1String("C"))
320 acceptLanguage = QString::fromLatin1("en,*");
321 else if (systemLocale.startsWith(QLatin1String("en-")))
322 acceptLanguage = systemLocale + QLatin1String(",*");
323 else
324 acceptLanguage = systemLocale + QLatin1String(",en,*");
325 request.setHeaderField("Accept-Language", std::move(acceptLanguage).toLatin1());
326 }
327
328 // set the User Agent
329 value = request.headerField("user-agent");
330 if (value.isEmpty())
331 request.setHeaderField("User-Agent", "Mozilla/5.0");
332 // set the host
333 value = request.headerField("host");
334 if (value.isEmpty()) {
335 QHostAddress add;
336 QByteArray host;
337 if (add.setAddress(hostName)) {
338 if (add.protocol() == QAbstractSocket::IPv6Protocol)
339 host = '[' + hostName.toLatin1() + ']'; //format the ipv6 in the standard way
340 else
341 host = hostName.toLatin1();
342
343 } else {
344 host = QUrl::toAce(hostName);
345 }
346
347 int port = request.url().port();
348 if (port != -1) {
349 host += ':';
350 host += QByteArray::number(port);
351 }
352
353 request.prependHeaderField("Host", host);
354 }
355
356 reply->d_func()->requestIsPrepared = true;
357}
358
359
360
361
362void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
363 QHttpNetworkReply *reply,
364 QNetworkReply::NetworkError errorCode)
365{
366 Q_Q(QHttpNetworkConnection);
367
368 int i = 0;
369 if (socket)
370 i = indexOf(socket);
371
372 if (reply) {
373 // this error matters only to this reply
374 reply->d_func()->errorString = errorDetail(errorCode, socket);
375 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
376 // remove the corrupt data if any
377 reply->d_func()->eraseData();
378
379 // Clean the channel
380 channels[i].close();
381 channels[i].reply = nullptr;
382 if (channels[i].protocolHandler)
383 channels[i].protocolHandler->setReply(nullptr);
384 channels[i].request = QHttpNetworkRequest();
385 if (socket)
386 channels[i].requeueCurrentlyPipelinedRequests();
387
388 // send the next request
389 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
390 }
391}
392
393void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
394{
395 Q_ASSERT(auth);
396
397 // NTLM and Negotiate do multi-phase authentication.
398 // Copying credentialsbetween authenticators would mess things up.
399 if (fromChannel >= 0) {
400 const QHttpNetworkConnectionChannel &channel = channels[fromChannel];
401 const QAuthenticatorPrivate::Method method = isProxy ? channel.proxyAuthMethod : channel.authMethod;
402 if (method == QAuthenticatorPrivate::Ntlm || method == QAuthenticatorPrivate::Negotiate)
403 return;
404 }
405
406 // select another channel
407 QAuthenticator* otherAuth = nullptr;
408 for (int i = 0; i < activeChannelCount; ++i) {
409 if (i == fromChannel)
410 continue;
411 if (isProxy)
412 otherAuth = &channels[i].proxyAuthenticator;
413 else
414 otherAuth = &channels[i].authenticator;
415 // if the credentials are different, copy them
416 if (otherAuth->user().compare(auth->user()))
417 otherAuth->setUser(auth->user());
418 if (otherAuth->password().compare(auth->password()))
419 otherAuth->setPassword(auth->password());
420 }
421}
422
423
424// handles the authentication for one channel and eventually re-starts the other channels
425bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
426 bool isProxy, bool &resend)
427{
428 Q_ASSERT(socket);
429 Q_ASSERT(reply);
430
431 resend = false;
432 //create the response header to be used with QAuthenticatorPrivate.
433 QList<QPair<QByteArray, QByteArray> > fields = reply->header();
434
435 //find out the type of authentication protocol requested.
436 QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
437 if (authMethod != QAuthenticatorPrivate::None) {
438 int i = indexOf(socket);
439 //Use a single authenticator for all domains. ### change later to use domain/realm
440 QAuthenticator* auth = nullptr;
441 if (isProxy) {
442 auth = &channels[i].proxyAuthenticator;
443 channels[i].proxyAuthMethod = authMethod;
444 } else {
445 auth = &channels[i].authenticator;
446 channels[i].authMethod = authMethod;
447 }
448 //proceed with the authentication.
449 if (auth->isNull())
450 auth->detach();
451 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
452 priv->parseHttpResponse(fields, isProxy, reply->url().host());
453 // Update method in case it changed
454 if (priv->method == QAuthenticatorPrivate::None)
455 return false;
456 if (isProxy)
457 channels[i].proxyAuthMethod = priv->method;
458 else
459 channels[i].authMethod = priv->method;
460
461 if (priv->phase == QAuthenticatorPrivate::Done) {
462 pauseConnection();
463 if (!isProxy) {
464 if (channels[i].authenticationCredentialsSent) {
465 auth->detach();
466 priv = QAuthenticatorPrivate::getPrivate(*auth);
467 priv->hasFailed = true;
468 priv->phase = QAuthenticatorPrivate::Done;
469 channels[i].authenticationCredentialsSent = false;
470 }
471 emit reply->authenticationRequired(reply->request(), auth);
472#ifndef QT_NO_NETWORKPROXY
473 } else {
474 if (channels[i].proxyCredentialsSent) {
475 auth->detach();
476 priv = QAuthenticatorPrivate::getPrivate(*auth);
477 priv->hasFailed = true;
478 priv->phase = QAuthenticatorPrivate::Done;
479 channels[i].proxyCredentialsSent = false;
480 }
481 emit reply->proxyAuthenticationRequired(networkProxy, auth);
482#endif
483 }
484 resumeConnection();
485
486 if (priv->phase != QAuthenticatorPrivate::Done) {
487 // send any pending requests
488 copyCredentials(i, auth, isProxy);
489 }
490 } else if (priv->phase == QAuthenticatorPrivate::Start) {
491 // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
492 // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
493 // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
494 emit reply->cacheCredentials(reply->request(), auth);
495 }
496 // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
497 // then nothing was filled in by the user or the cache
498 // - If withCredentials has been set to false (e.g. by Qt WebKit for a cross-origin XMLHttpRequest) then
499 // we need to bail out if authentication is required.
500 if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
501 // Reset authenticator so the next request on that channel does not get messed up
502 auth = nullptr;
503 if (isProxy)
504 channels[i].proxyAuthenticator = QAuthenticator();
505 else
506 channels[i].authenticator = QAuthenticator();
507
508 // authentication is cancelled, send the current contents to the user.
509 emit channels[i].reply->headerChanged();
510 emit channels[i].reply->readyRead();
511 QNetworkReply::NetworkError errorCode =
512 isProxy
513 ? QNetworkReply::ProxyAuthenticationRequiredError
514 : QNetworkReply::AuthenticationRequiredError;
515 reply->d_func()->errorString = errorDetail(errorCode, socket);
516 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
517 // ### at this point the reply could be deleted
518 return true;
519 }
520 //resend the request
521 resend = true;
522 return true;
523 }
524 return false;
525}
526
527QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply)
528{
529 if (!reply->request().isFollowRedirects())
530 return QUrl();
531
532 QUrl redirectUrl;
533 const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
534 for (const QNetworkReply::RawHeaderPair &header : fields) {
535 if (header.first.compare("location", Qt::CaseInsensitive) == 0) {
536 redirectUrl = QUrl::fromEncoded(header.second);
537 break;
538 }
539 }
540
541 // If the location url is invalid/empty, we emit ProtocolUnknownError
542 if (!redirectUrl.isValid()) {
543 emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
544 return QUrl();
545 }
546
547 // Check if we have exceeded max redirects allowed
548 if (reply->request().redirectCount() <= 0) {
549 emitReplyError(socket, reply, QNetworkReply::TooManyRedirectsError);
550 return QUrl();
551 }
552
553 // Resolve the URL if it's relative
554 if (redirectUrl.isRelative())
555 redirectUrl = reply->request().url().resolved(redirectUrl);
556
557 // Check redirect url protocol
558 const QUrl priorUrl(reply->request().url());
559 if (redirectUrl.scheme() == QLatin1String("http") || redirectUrl.scheme() == QLatin1String("https")) {
560 switch (reply->request().redirectPolicy()) {
561 case QNetworkRequest::NoLessSafeRedirectPolicy:
562 // Here we could handle https->http redirects as InsecureProtocolError.
563 // However, if HSTS is enabled and redirectUrl.host() is a known STS
564 // host, then we'll replace its scheme and this won't downgrade protocol,
565 // after all. We cannot access QNAM's STS cache from here, so delegate
566 // this check to QNetworkReplyHttpImpl.
567 break;
568 case QNetworkRequest::SameOriginRedirectPolicy:
569 if (priorUrl.host() != redirectUrl.host()
570 || priorUrl.scheme() != redirectUrl.scheme()
571 || priorUrl.port() != redirectUrl.port()) {
572 emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
573 return QUrl();
574 }
575 break;
576 case QNetworkRequest::UserVerifiedRedirectPolicy:
577 break;
578 default:
579 Q_ASSERT(!"Unexpected redirect policy");
580 }
581 } else {
582 emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
583 return QUrl();
584 }
585 return redirectUrl;
586}
587
588void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
589{
590 Q_ASSERT(socket);
591
592 int i = indexOf(socket);
593
594 // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
595 if (channels[i].authMethod != QAuthenticatorPrivate::None) {
596 if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
597 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
598 if (priv && priv->method != QAuthenticatorPrivate::None) {
599 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host());
600 request.setHeaderField("Authorization", response);
601 channels[i].authenticationCredentialsSent = true;
602 }
603 }
604 }
605
606#if QT_CONFIG(networkproxy)
607 // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
608 if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
609 if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
610 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
611 if (priv && priv->method != QAuthenticatorPrivate::None) {
612 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName());
613 request.setHeaderField("Proxy-Authorization", response);
614 channels[i].proxyCredentialsSent = true;
615 }
616 }
617 }
618#endif // QT_CONFIG(networkproxy)
619}
620
621QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
622{
623 Q_Q(QHttpNetworkConnection);
624
625 // The reply component of the pair is created initially.
626 QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
627 reply->setRequest(request);
628 reply->d_func()->connection = q;
629 reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
630 HttpMessagePair pair = qMakePair(request, reply);
631
632 if (request.isPreConnect())
633 preConnectRequests++;
634
635 if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP
636 || (!encrypt && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 && !channels[0].switchedToHttp2)) {
637 switch (request.priority()) {
638 case QHttpNetworkRequest::HighPriority:
639 highPriorityQueue.prepend(pair);
640 break;
641 case QHttpNetworkRequest::NormalPriority:
642 case QHttpNetworkRequest::LowPriority:
643 lowPriorityQueue.prepend(pair);
644 break;
645 }
646 }
647 else { // HTTP/2 ('h2' mode)
648 if (!pair.second->d_func()->requestIsPrepared)
649 prepareRequest(pair);
650 channels[0].h2RequestsToSend.insert(request.priority(), pair);
651 }
652
653 // For Happy Eyeballs the networkLayerState is set to Unknown
654 // until we have started the first connection attempt. So no
655 // request will be started until we know if IPv4 or IPv6
656 // should be used.
657 if (networkLayerState == Unknown || networkLayerState == HostLookupPending) {
658 startHostInfoLookup();
659 } else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
660 // this used to be called via invokeMethod and a QueuedConnection
661 // It is the only place _q_startNextRequest is called directly without going
662 // through the event loop using a QueuedConnection.
663 // This is dangerous because of recursion that might occur when emitting
664 // signals as DirectConnection from this code path. Therefore all signal
665 // emissions that can come out from this code path need to
666 // be QueuedConnection.
667 // We are currently trying to fine-tune this.
668 _q_startNextRequest();
669 }
670 return reply;
671}
672
673void QHttpNetworkConnectionPrivate::fillHttp2Queue()
674{
675 for (auto &pair : highPriorityQueue) {
676 if (!pair.second->d_func()->requestIsPrepared)
677 prepareRequest(pair);
678 channels[0].h2RequestsToSend.insert(QHttpNetworkRequest::HighPriority, pair);
679 }
680
681 highPriorityQueue.clear();
682
683 for (auto &pair : lowPriorityQueue) {
684 if (!pair.second->d_func()->requestIsPrepared)
685 prepareRequest(pair);
686 channels[0].h2RequestsToSend.insert(pair.first.priority(), pair);
687 }
688
689 lowPriorityQueue.clear();
690}
691
692void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
693{
694 Q_Q(QHttpNetworkConnection);
695
696 QHttpNetworkRequest request = pair.first;
697 switch (request.priority()) {
698 case QHttpNetworkRequest::HighPriority:
699 highPriorityQueue.prepend(pair);
700 break;
701 case QHttpNetworkRequest::NormalPriority:
702 case QHttpNetworkRequest::LowPriority:
703 lowPriorityQueue.prepend(pair);
704 break;
705 }
706
707 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
708}
709
710bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
711{
712 int i = 0;
713 if (socket)
714 i = indexOf(socket);
715
716 if (!highPriorityQueue.isEmpty()) {
717 // remove from queue before sendRequest! else we might pipeline the same request again
718 HttpMessagePair messagePair = highPriorityQueue.takeLast();
719 if (!messagePair.second->d_func()->requestIsPrepared)
720 prepareRequest(messagePair);
721 updateChannel(i, messagePair);
722 return true;
723 }
724
725 if (!lowPriorityQueue.isEmpty()) {
726 // remove from queue before sendRequest! else we might pipeline the same request again
727 HttpMessagePair messagePair = lowPriorityQueue.takeLast();
728 if (!messagePair.second->d_func()->requestIsPrepared)
729 prepareRequest(messagePair);
730 updateChannel(i, messagePair);
731 return true;
732 }
733 return false;
734}
735
736void QHttpNetworkConnectionPrivate::updateChannel(int i, const HttpMessagePair &messagePair)
737{
738 channels[i].request = messagePair.first;
739 channels[i].reply = messagePair.second;
740 // Now that reply is assigned a channel, correct reply to channel association
741 // previously set in queueRequest.
742 channels[i].reply->d_func()->connectionChannel = &channels[i];
743}
744
745QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest() const
746{
747 if (!highPriorityQueue.isEmpty())
748 return highPriorityQueue.last().first;
749 if (!lowPriorityQueue.isEmpty())
750 return lowPriorityQueue.last().first;
751 return QHttpNetworkRequest();
752}
753
754// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
755void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
756{
757 // return fast if there is nothing to pipeline
758 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
759 return;
760
761 int i = indexOf(socket);
762
763 // return fast if there was no reply right now processed
764 if (channels[i].reply == nullptr)
765 return;
766
767 if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
768 return;
769 }
770
771 if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
772 return;
773
774 // the current request that is in must already support pipelining
775 if (!channels[i].request.isPipeliningAllowed())
776 return;
777
778 // the current request must be a idempotent (right now we only check GET)
779 if (channels[i].request.operation() != QHttpNetworkRequest::Get)
780 return;
781
782 // check if socket is connected
783 if (socket->state() != QAbstractSocket::ConnectedState)
784 return;
785
786 // check for resendCurrent
787 if (channels[i].resendCurrent)
788 return;
789
790 // we do not like authentication stuff
791 // ### make sure to be OK with this in later releases
792 if (!channels[i].authenticator.isNull()
793 && (!channels[i].authenticator.user().isEmpty()
794 || !channels[i].authenticator.password().isEmpty()))
795 return;
796 if (!channels[i].proxyAuthenticator.isNull()
797 && (!channels[i].proxyAuthenticator.user().isEmpty()
798 || !channels[i].proxyAuthenticator.password().isEmpty()))
799 return;
800
801 // must be in ReadingState or WaitingState
802 if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
803 || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
804 return;
805
806 int lengthBefore;
807 while (!highPriorityQueue.isEmpty()) {
808 lengthBefore = channels[i].alreadyPipelinedRequests.length();
809 fillPipeline(highPriorityQueue, channels[i]);
810
811 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
812 channels[i].pipelineFlush();
813 return;
814 }
815
816 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
817 break; // did not process anything, now do the low prio queue
818 }
819
820 while (!lowPriorityQueue.isEmpty()) {
821 lengthBefore = channels[i].alreadyPipelinedRequests.length();
822 fillPipeline(lowPriorityQueue, channels[i]);
823
824 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
825 channels[i].pipelineFlush();
826 return;
827 }
828
829 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
830 break; // did not process anything
831 }
832
833
834 channels[i].pipelineFlush();
835}
836
837// returns true when the processing of a queue has been done
838bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
839{
840 if (queue.isEmpty())
841 return true;
842
843 for (int i = queue.count() - 1; i >= 0; --i) {
844 HttpMessagePair messagePair = queue.at(i);
845 const QHttpNetworkRequest &request = messagePair.first;
846
847 // we currently do not support pipelining if HTTP authentication is used
848 if (!request.url().userInfo().isEmpty())
849 continue;
850
851 // take only GET requests
852 if (request.operation() != QHttpNetworkRequest::Get)
853 continue;
854
855 if (!request.isPipeliningAllowed())
856 continue;
857
858 // remove it from the queue
859 queue.takeAt(i);
860 // we modify the queue we iterate over here, but since we return from the function
861 // afterwards this is fine.
862
863 // actually send it
864 if (!messagePair.second->d_func()->requestIsPrepared)
865 prepareRequest(messagePair);
866 channel.pipelineInto(messagePair);
867
868 // return false because we processed something and need to process again
869 return false;
870 }
871
872 // return true, the queue has been processed and not changed
873 return true;
874}
875
876
877QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail)
878{
879 QString errorString;
880 switch (errorCode) {
881 case QNetworkReply::HostNotFoundError:
882 if (socket)
883 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
884 else
885 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(hostName);
886 break;
887 case QNetworkReply::ConnectionRefusedError:
888 errorString = QCoreApplication::translate("QHttp", "Connection refused");
889 break;
890 case QNetworkReply::RemoteHostClosedError:
891 errorString = QCoreApplication::translate("QHttp", "Connection closed");
892 break;
893 case QNetworkReply::TimeoutError:
894 errorString = QCoreApplication::translate("QAbstractSocket", "Socket operation timed out");
895 break;
896 case QNetworkReply::ProxyAuthenticationRequiredError:
897 errorString = QCoreApplication::translate("QHttp", "Proxy requires authentication");
898 break;
899 case QNetworkReply::AuthenticationRequiredError:
900 errorString = QCoreApplication::translate("QHttp", "Host requires authentication");
901 break;
902 case QNetworkReply::ProtocolFailure:
903 errorString = QCoreApplication::translate("QHttp", "Data corrupted");
904 break;
905 case QNetworkReply::ProtocolUnknownError:
906 errorString = QCoreApplication::translate("QHttp", "Unknown protocol specified");
907 break;
908 case QNetworkReply::SslHandshakeFailedError:
909 errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
910 break;
911 case QNetworkReply::TooManyRedirectsError:
912 errorString = QCoreApplication::translate("QHttp", "Too many redirects");
913 break;
914 case QNetworkReply::InsecureRedirectError:
915 errorString = QCoreApplication::translate("QHttp", "Insecure redirect");
916 break;
917 default:
918 // all other errors are treated as QNetworkReply::UnknownNetworkError
919 errorString = extraDetail;
920 break;
921 }
922 return errorString;
923}
924
925// this is called from the destructor of QHttpNetworkReply. It is called when
926// the reply was finished correctly or when it was aborted.
927void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
928{
929 Q_Q(QHttpNetworkConnection);
930
931 // check if the reply is currently being processed or it is pipelined in
932 for (int i = 0; i < activeChannelCount; ++i) {
933 // is the reply associated the currently processing of this channel?
934 if (channels[i].reply == reply) {
935 channels[i].reply = nullptr;
936 if (channels[i].protocolHandler)
937 channels[i].protocolHandler->setReply(nullptr);
938 channels[i].request = QHttpNetworkRequest();
939 channels[i].resendCurrent = false;
940
941 if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
942 // the reply had to be prematurely removed, e.g. it was not finished
943 // therefore we have to requeue the already pipelined requests.
944 channels[i].requeueCurrentlyPipelinedRequests();
945 }
946
947 // if HTTP mandates we should close
948 // or the reply is not finished yet, e.g. it was aborted
949 // we have to close that connection
950 if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished()) {
951 if (reply->isAborted()) {
952 channels[i].abort();
953 } else {
954 channels[i].close();
955 }
956 }
957
958 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
959 return;
960 }
961
962 // is the reply inside the pipeline of this channel already?
963 for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
964 if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
965 // Remove that HttpMessagePair
966 channels[i].alreadyPipelinedRequests.removeAt(j);
967
968 channels[i].requeueCurrentlyPipelinedRequests();
969
970 // Since some requests had already been pipelined, but we removed
971 // one and re-queued the others
972 // we must force a connection close after the request that is
973 // currently in processing has been finished.
974 if (channels[i].reply)
975 channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
976
977 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
978 return;
979 }
980 }
981#ifndef QT_NO_SSL
982 // is the reply inside the H2 pipeline of this channel already?
983 QMultiMap<int, HttpMessagePair>::iterator it = channels[i].h2RequestsToSend.begin();
984 QMultiMap<int, HttpMessagePair>::iterator end = channels[i].h2RequestsToSend.end();
985 for (; it != end; ++it) {
986 if (it.value().second == reply) {
987 channels[i].h2RequestsToSend.remove(it.key());
988
989 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
990 return;
991 }
992 }
993#endif
994 }
995 // remove from the high priority queue
996 if (!highPriorityQueue.isEmpty()) {
997 for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
998 HttpMessagePair messagePair = highPriorityQueue.at(j);
999 if (messagePair.second == reply) {
1000 highPriorityQueue.removeAt(j);
1001 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1002 return;
1003 }
1004 }
1005 }
1006 // remove from the low priority queue
1007 if (!lowPriorityQueue.isEmpty()) {
1008 for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
1009 HttpMessagePair messagePair = lowPriorityQueue.at(j);
1010 if (messagePair.second == reply) {
1011 lowPriorityQueue.removeAt(j);
1012 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1013 return;
1014 }
1015 }
1016 }
1017}
1018
1019
1020
1021// This function must be called from the event loop. The only
1022// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
1023// although it is called _q_startNextRequest, it will actually start multiple requests when possible
1024void QHttpNetworkConnectionPrivate::_q_startNextRequest()
1025{
1026 // If there is no network layer state decided we should not start any new requests.
1027 if (networkLayerState == Unknown || networkLayerState == HostLookupPending || networkLayerState == IPv4or6)
1028 return;
1029
1030 // If the QHttpNetworkConnection is currently paused then bail out immediately
1031 if (state == PausedState)
1032 return;
1033
1034 //resend the necessary ones.
1035 for (int i = 0; i < activeChannelCount; ++i) {
1036 if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
1037 channels[i].resendCurrent = false;
1038
1039 // if this is not possible, error will be emitted and connection terminated
1040 if (!channels[i].resetUploadData())
1041 continue;
1042 channels[i].sendRequest();
1043 }
1044 }
1045
1046 // dequeue new ones
1047
1048 switch (connectionType) {
1049 case QHttpNetworkConnection::ConnectionTypeHTTP: {
1050 // return fast if there is nothing to do
1051 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
1052 return;
1053
1054 // try to get a free AND connected socket
1055 for (int i = 0; i < activeChannelCount; ++i) {
1056 if (channels[i].socket) {
1057 if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
1058 if (dequeueRequest(channels[i].socket))
1059 channels[i].sendRequest();
1060 }
1061 }
1062 }
1063 break;
1064 }
1065 case QHttpNetworkConnection::ConnectionTypeHTTP2Direct:
1066 case QHttpNetworkConnection::ConnectionTypeHTTP2: {
1067 if (channels[0].h2RequestsToSend.isEmpty() && channels[0].switchedToHttp2)
1068 return;
1069
1070 if (networkLayerState == IPv4)
1071 channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1072 else if (networkLayerState == IPv6)
1073 channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1074 channels[0].ensureConnection();
1075 if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
1076 && !channels[0].pendingEncrypt) {
1077 if (channels[0].h2RequestsToSend.size()) {
1078 channels[0].sendRequest();
1079 } else if (!channels[0].reply && !channels[0].switchedToHttp2) {
1080 // This covers an edge-case where we're already connected and the "connected"
1081 // signal was already sent, but we didn't have any request available at the time,
1082 // so it was missed. As such we need to dequeue a request and send it now that we
1083 // have one.
1084 dequeueRequest(channels[0].socket);
1085 channels[0].sendRequest();
1086 }
1087 }
1088 break;
1089 }
1090 }
1091
1092 // try to push more into all sockets
1093 // ### FIXME we should move this to the beginning of the function
1094 // as soon as QtWebkit is properly using the pipelining
1095 // (e.g. not for XMLHttpRequest or the first page load)
1096 // ### FIXME we should also divide the requests more even
1097 // on the connected sockets
1098 //tryToFillPipeline(socket);
1099 // return fast if there is nothing to pipeline
1100 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
1101 return;
1102 for (int i = 0; i < activeChannelCount; i++)
1103 if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState)
1104 fillPipeline(channels[i].socket);
1105
1106 // If there is not already any connected channels we need to connect a new one.
1107 // We do not pair the channel with the request until we know if it is
1108 // connected or not. This is to reuse connected channels before we connect new once.
1109 int queuedRequests = highPriorityQueue.count() + lowPriorityQueue.count();
1110
1111 // in case we have in-flight preconnect requests and normal requests,
1112 // we only need one socket for each (preconnect, normal request) pair
1113 int neededOpenChannels = queuedRequests;
1114 if (preConnectRequests > 0) {
1115 int normalRequests = queuedRequests - preConnectRequests;
1116 neededOpenChannels = qMax(normalRequests, preConnectRequests);
1117 }
1118 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1119 bool connectChannel = false;
1120 if (channels[i].socket) {
1121 if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
1122 || (channels[i].socket->state() == QAbstractSocket::HostLookupState)
1123 || channels[i].pendingEncrypt) // pendingEncrypt == "EncryptingState"
1124 neededOpenChannels--;
1125
1126 if (neededOpenChannels <= 0)
1127 break;
1128 if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState))
1129 connectChannel = true;
1130 } else { // not previously used channel
1131 connectChannel = true;
1132 }
1133
1134 if (connectChannel) {
1135 if (networkLayerState == IPv4)
1136 channels[i].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1137 else if (networkLayerState == IPv6)
1138 channels[i].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1139 channels[i].ensureConnection();
1140 neededOpenChannels--;
1141 }
1142 }
1143}
1144
1145
1146void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
1147{
1148 for (int i = 0 ; i < activeChannelCount; ++i) {
1149 if (channels[i].reply == reply) {
1150 // emulate a readyRead() from the socket
1151 QMetaObject::invokeMethod(&channels[i], "_q_readyRead", Qt::QueuedConnection);
1152 return;
1153 }
1154 }
1155}
1156
1157
1158
1159// The first time we start the connection is used we do not know if we
1160// should use IPv4 or IPv6. So we start a hostlookup to figure this out.
1161// Later when we do the connection the socket will not need to do another
1162// lookup as then the hostinfo will already be in the cache.
1163void QHttpNetworkConnectionPrivate::startHostInfoLookup()
1164{
1165 networkLayerState = HostLookupPending;
1166
1167 // check if we already now can decide if this is IPv4 or IPv6
1168 QString lookupHost = hostName;
1169#ifndef QT_NO_NETWORKPROXY
1170 if (networkProxy.capabilities() & QNetworkProxy::HostNameLookupCapability) {
1171 lookupHost = networkProxy.hostName();
1172 } else if (channels[0].proxy.capabilities() & QNetworkProxy::HostNameLookupCapability) {
1173 lookupHost = channels[0].proxy.hostName();
1174 }
1175#endif
1176 QHostAddress temp;
1177 if (temp.setAddress(lookupHost)) {
1178 const QAbstractSocket::NetworkLayerProtocol protocol = temp.protocol();
1179 if (protocol == QAbstractSocket::IPv4Protocol) {
1180 networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1181 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1182 return;
1183 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1184 networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1185 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1186 return;
1187 }
1188 } else {
1189 int hostLookupId;
1190 bool immediateResultValid = false;
1191 QHostInfo hostInfo = qt_qhostinfo_lookup(lookupHost,
1192 this->q_func(),
1193 SLOT(_q_hostLookupFinished(QHostInfo)),
1194 &immediateResultValid,
1195 &hostLookupId);
1196 if (immediateResultValid) {
1197 _q_hostLookupFinished(hostInfo);
1198 }
1199 }
1200}
1201
1202
1203void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
1204{
1205 bool bIpv4 = false;
1206 bool bIpv6 = false;
1207 bool foundAddress = false;
1208 if (networkLayerState == IPv4 || networkLayerState == IPv6 || networkLayerState == IPv4or6)
1209 return;
1210
1211 const auto addresses = info.addresses();
1212 for (const QHostAddress &address : addresses) {
1213 const QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
1214 if (protocol == QAbstractSocket::IPv4Protocol) {
1215 if (!foundAddress) {
1216 foundAddress = true;
1217 delayIpv4 = false;
1218 }
1219 bIpv4 = true;
1220 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1221 if (!foundAddress) {
1222 foundAddress = true;
1223 delayIpv4 = true;
1224 }
1225 bIpv6 = true;
1226 }
1227 }
1228
1229 if (bIpv4 && bIpv6)
1230 startNetworkLayerStateLookup();
1231 else if (bIpv4) {
1232 networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1233 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1234 } else if (bIpv6) {
1235 networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1236 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1237 } else {
1238 if (dequeueRequest(channels[0].socket)) {
1239 emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
1240 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1241 } else if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
1242 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1243 for (const HttpMessagePair &h2Pair : qAsConst(channels[0].h2RequestsToSend)) {
1244 // emit error for all replies
1245 QHttpNetworkReply *currentReply = h2Pair.second;
1246 Q_ASSERT(currentReply);
1247 emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError);
1248 }
1249 } else {
1250 // Should not happen: we start a host lookup before sending a request,
1251 // so it's natural to have requests either in HTTP/2 queue, or in low/high
1252 // priority queues.
1253 qWarning("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
1254 " could not de-queue request, failed to report HostNotFoundError");
1255 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1256 }
1257 }
1258}
1259
1260
1261// This will be used if the host lookup found both and Ipv4 and
1262// Ipv6 address. Then we will start up two connections and pick
1263// the network layer of the one that finish first. The second
1264// connection will then be disconnected.
1265void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
1266{
1267 if (activeChannelCount > 1) {
1268 // At this time all channels should be unconnected.
1269 Q_ASSERT(!channels[0].isSocketBusy());
1270 Q_ASSERT(!channels[1].isSocketBusy());
1271
1272 networkLayerState = IPv4or6;
1273
1274 channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1275 channels[1].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1276
1277 int timeout = 300;
1278 delayedConnectionTimer.start(timeout);
1279 if (delayIpv4)
1280 channels[1].ensureConnection();
1281 else
1282 channels[0].ensureConnection();
1283 } else {
1284 networkLayerState = IPv4or6;
1285 channels[0].networkLayerPreference = QAbstractSocket::AnyIPProtocol;
1286 channels[0].ensureConnection();
1287 }
1288}
1289
1290void QHttpNetworkConnectionPrivate::networkLayerDetected(QAbstractSocket::NetworkLayerProtocol protocol)
1291{
1292 for (int i = 0 ; i < activeChannelCount; ++i) {
1293 if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
1294 channels[i].close();
1295 }
1296 }
1297}
1298
1299void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
1300{
1301 if (delayIpv4)
1302 channels[0].ensureConnection();
1303 else
1304 channels[1].ensureConnection();
1305}
1306
1307QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
1308 QHttpNetworkConnection::ConnectionType connectionType, QObject *parent)
1309 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt , connectionType)), parent)
1310{
1311 Q_D(QHttpNetworkConnection);
1312 d->init();
1313 if (QNetworkStatusMonitor::isEnabled()) {
1314 connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
1315 this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
1316 }
1317}
1318
1319QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
1320 quint16 port, bool encrypt, QObject *parent,
1321 QHttpNetworkConnection::ConnectionType connectionType)
1322 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
1323 connectionType)), parent)
1324{
1325 Q_D(QHttpNetworkConnection);
1326 d->init();
1327 if (QNetworkStatusMonitor::isEnabled()) {
1328 connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
1329 this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
1330 }
1331}
1332
1333QHttpNetworkConnection::~QHttpNetworkConnection()
1334{
1335}
1336
1337QString QHttpNetworkConnection::hostName() const
1338{
1339 Q_D(const QHttpNetworkConnection);
1340 return d->hostName;
1341}
1342
1343quint16 QHttpNetworkConnection::port() const
1344{
1345 Q_D(const QHttpNetworkConnection);
1346 return d->port;
1347}
1348
1349QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
1350{
1351 Q_D(QHttpNetworkConnection);
1352 return d->queueRequest(request);
1353}
1354
1355void QHttpNetworkConnection::fillHttp2Queue()
1356{
1357 Q_D(QHttpNetworkConnection);
1358 d->fillHttp2Queue();
1359}
1360
1361bool QHttpNetworkConnection::isSsl() const
1362{
1363 Q_D(const QHttpNetworkConnection);
1364 return d->encrypt;
1365}
1366
1367QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const
1368{
1369 return d_func()->channels;
1370}
1371
1372#ifndef QT_NO_NETWORKPROXY
1373void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
1374{
1375 Q_D(QHttpNetworkConnection);
1376 d->networkProxy = networkProxy;
1377 // update the authenticator
1378 if (!d->networkProxy.user().isEmpty()) {
1379 for (int i = 0; i < d->activeChannelCount; ++i) {
1380 d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
1381 d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
1382 }
1383 }
1384}
1385
1386QNetworkProxy QHttpNetworkConnection::cacheProxy() const
1387{
1388 Q_D(const QHttpNetworkConnection);
1389 return d->networkProxy;
1390}
1391
1392void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
1393{
1394 Q_D(QHttpNetworkConnection);
1395 for (int i = 0; i < d->activeChannelCount; ++i)
1396 d->channels[i].setProxy(networkProxy);
1397}
1398
1399QNetworkProxy QHttpNetworkConnection::transparentProxy() const
1400{
1401 Q_D(const QHttpNetworkConnection);
1402 return d->channels[0].proxy;
1403}
1404#endif
1405
1406QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType()
1407{
1408 Q_D(QHttpNetworkConnection);
1409 return d->connectionType;
1410}
1411
1412void QHttpNetworkConnection::setConnectionType(ConnectionType type)
1413{
1414 Q_D(QHttpNetworkConnection);
1415 d->connectionType = type;
1416}
1417
1418QHttp2Configuration QHttpNetworkConnection::http2Parameters() const
1419{
1420 Q_D(const QHttpNetworkConnection);
1421 return d->http2Parameters;
1422}
1423
1424void QHttpNetworkConnection::setHttp2Parameters(const QHttp2Configuration &params)
1425{
1426 Q_D(QHttpNetworkConnection);
1427 d->http2Parameters = params;
1428}
1429
1430// SSL support below
1431#ifndef QT_NO_SSL
1432void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
1433{
1434 Q_D(QHttpNetworkConnection);
1435 if (!d->encrypt)
1436 return;
1437
1438 // set the config on all channels
1439 for (int i = 0; i < d->activeChannelCount; ++i)
1440 d->channels[i].setSslConfiguration(config);
1441}
1442
1443QSharedPointer<QSslContext> QHttpNetworkConnection::sslContext()
1444{
1445 Q_D(QHttpNetworkConnection);
1446 return d->sslContext;
1447}
1448
1449void QHttpNetworkConnection::setSslContext(QSharedPointer<QSslContext> context)
1450{
1451 Q_D(QHttpNetworkConnection);
1452 d->sslContext = std::move(context);
1453}
1454
1455void QHttpNetworkConnection::ignoreSslErrors(int channel)
1456{
1457 Q_D(QHttpNetworkConnection);
1458 if (!d->encrypt)
1459 return;
1460
1461 if (channel == -1) { // ignore for all channels
1462 // We need to ignore for all channels, even the ones that are not in use just in case they
1463 // will be in the future.
1464 for (int i = 0; i < d->channelCount; ++i) {
1465 d->channels[i].ignoreSslErrors();
1466 }
1467
1468 } else {
1469 d->channels[channel].ignoreSslErrors();
1470 }
1471}
1472
1473void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
1474{
1475 Q_D(QHttpNetworkConnection);
1476 if (!d->encrypt)
1477 return;
1478
1479 if (channel == -1) { // ignore for all channels
1480 // We need to ignore for all channels, even the ones that are not in use just in case they
1481 // will be in the future.
1482 for (int i = 0; i < d->channelCount; ++i) {
1483 d->channels[i].ignoreSslErrors(errors);
1484 }
1485
1486 } else {
1487 d->channels[channel].ignoreSslErrors(errors);
1488 }
1489}
1490
1491#endif //QT_NO_SSL
1492
1493void QHttpNetworkConnection::preConnectFinished()
1494{
1495 d_func()->preConnectRequests--;
1496}
1497
1498QString QHttpNetworkConnection::peerVerifyName() const
1499{
1500 Q_D(const QHttpNetworkConnection);
1501 return d->peerVerifyName;
1502}
1503
1504void QHttpNetworkConnection::setPeerVerifyName(const QString &peerName)
1505{
1506 Q_D(QHttpNetworkConnection);
1507 d->peerVerifyName = peerName;
1508}
1509
1510void QHttpNetworkConnection::onlineStateChanged(bool isOnline)
1511{
1512 Q_D(QHttpNetworkConnection);
1513
1514 if (isOnline) {
1515 // If we did not have any 'isOffline' previously - well, good
1516 // to know, we are 'online' apparently.
1517 return;
1518 }
1519
1520 for (int i = 0; i < d->activeChannelCount; i++) {
1521 auto &channel = d->channels[i];
1522 channel.emitFinishedWithError(QNetworkReply::TemporaryNetworkFailureError, "Temporary network failure.");
1523 channel.close();
1524 }
1525
1526 // We don't care, this connection is broken from our POV.
1527 d->connectionMonitor.stopMonitoring();
1528}
1529
1530#ifndef QT_NO_NETWORKPROXY
1531// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
1532// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
1533// e.g. it is for SOCKS proxies which require authentication.
1534void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
1535{
1536 // Also pause the connection because socket notifiers may fire while an user
1537 // dialog is displaying
1538 pauseConnection();
1539 QHttpNetworkReply *reply;
1540 if ((connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
1541 && (chan->switchedToHttp2 || chan->h2RequestsToSend.count() > 0))
1542 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1543 // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
1544 // but that does not matter because the signal will ultimately be emitted
1545 // by the QNetworkAccessManager.
1546 Q_ASSERT(chan->h2RequestsToSend.count() > 0);
1547 reply = chan->h2RequestsToSend.cbegin().value().second;
1548 } else { // HTTP
1549 reply = chan->reply;
1550 }
1551
1552 Q_ASSERT(reply);
1553 emit reply->proxyAuthenticationRequired(proxy, auth);
1554 resumeConnection();
1555 int i = indexOf(chan->socket);
1556 copyCredentials(i, auth, true);
1557}
1558#endif
1559
1560
1561QT_END_NAMESPACE
1562
1563#include "moc_qhttpnetworkconnection_p.cpp"
1564