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