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(sender: channels[i].socket, signal: nullptr, receiver: &channels[i], member: 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 // ### Qt6: Remove section
139 //push session down to channels
140 channels[i].networkSession = networkSession;
141#endif
142 }
143
144 delayedConnectionTimer.setSingleShot(true);
145 QObject::connect(sender: &delayedConnectionTimer, SIGNAL(timeout()), receiver: 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(obj: &channels[i], member: "_q_uploadDataReadyRead", type: Qt::QueuedConnection);
181 }
182 }
183
184 // queue _q_startNextRequest
185 QMetaObject::invokeMethod(obj: this->q_func(), member: "_q_startNextRequest", type: 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(msg: "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(obj: q, member: "_q_startNextRequest", type: Qt::QueuedConnection);
240 emitError = false;
241 }
242 if (networkLayerState == QHttpNetworkConnectionPrivate::Unknown)
243 qWarning(msg: "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(a: uploadDeviceSize, b: 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(msg: "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(name: "proxy-connection");
288 if (value.isEmpty())
289 request.setHeaderField(name: "Proxy-Connection", data: "Keep-Alive");
290 } else {
291#endif
292 value = request.headerField(name: "connection");
293 if (value.isEmpty())
294 request.setHeaderField(name: "Connection", data: "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(name: "accept-encoding");
305 if (value.isEmpty()) {
306#ifndef QT_NO_COMPRESS
307 request.setHeaderField(name: "Accept-Encoding", data: "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(name: "accept-language");
320 if (value.isEmpty()) {
321 QString systemLocale = QLocale::system().name().replace(before: QChar::fromLatin1(c: '_'),after: QChar::fromLatin1(c: '-'));
322 QString acceptLanguage;
323 if (systemLocale == QLatin1String("C"))
324 acceptLanguage = QString::fromLatin1(str: "en,*");
325 else if (systemLocale.startsWith(s: QLatin1String("en-")))
326 acceptLanguage = systemLocale + QLatin1String(",*");
327 else
328 acceptLanguage = systemLocale + QLatin1String(",en,*");
329 request.setHeaderField(name: "Accept-Language", data: std::move(acceptLanguage).toLatin1());
330 }
331
332 // set the User Agent
333 value = request.headerField(name: "user-agent");
334 if (value.isEmpty())
335 request.setHeaderField(name: "User-Agent", data: "Mozilla/5.0");
336 // set the host
337 value = request.headerField(name: "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(name: "Host", data: 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, detail: 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 = nullptr;
386 if (channels[i].protocolHandler)
387 channels[i].protocolHandler->setReply(nullptr);
388 channels[i].request = QHttpNetworkRequest();
389 if (socket)
390 channels[i].requeueCurrentlyPipelinedRequests();
391
392 // send the next request
393 QMetaObject::invokeMethod(obj: q, member: "_q_startNextRequest", type: 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 = nullptr;
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(s: auth->user()))
421 otherAuth->setUser(auth->user());
422 if (otherAuth->password().compare(s: 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 = nullptr;
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&: *auth);
456 priv->parseHttpResponse(fields, isProxy, host: reply->url().host());
457 // Update method in case it changed
458 if (priv->method == QAuthenticatorPrivate::None)
459 return false;
460 if (isProxy)
461 channels[i].proxyAuthMethod = priv->method;
462 else
463 channels[i].authMethod = priv->method;
464
465 if (priv->phase == QAuthenticatorPrivate::Done ||
466 (priv->phase == QAuthenticatorPrivate::Start
467 && priv->method == QAuthenticatorPrivate::Ntlm)) {
468 if (priv->phase == QAuthenticatorPrivate::Start)
469 priv->phase = QAuthenticatorPrivate::Phase1;
470
471 pauseConnection();
472 if (!isProxy) {
473 if (channels[i].authenticationCredentialsSent) {
474 auth->detach();
475 priv = QAuthenticatorPrivate::getPrivate(auth&: *auth);
476 priv->hasFailed = true;
477 priv->phase = QAuthenticatorPrivate::Done;
478 channels[i].authenticationCredentialsSent = false;
479 }
480 emit reply->authenticationRequired(request: reply->request(), authenticator: auth);
481#ifndef QT_NO_NETWORKPROXY
482 } else {
483 if (channels[i].proxyCredentialsSent) {
484 auth->detach();
485 priv = QAuthenticatorPrivate::getPrivate(auth&: *auth);
486 priv->hasFailed = true;
487 priv->phase = QAuthenticatorPrivate::Done;
488 channels[i].proxyCredentialsSent = false;
489 }
490 emit reply->proxyAuthenticationRequired(proxy: networkProxy, authenticator: auth);
491#endif
492 }
493 resumeConnection();
494
495 if (priv->phase != QAuthenticatorPrivate::Done) {
496 // send any pending requests
497 copyCredentials(fromChannel: i, auth, isProxy);
498 }
499 } else if (priv->phase == QAuthenticatorPrivate::Start) {
500 // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
501 // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
502 // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
503 emit reply->cacheCredentials(request: reply->request(), authenticator: auth);
504 }
505 // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
506 // then nothing was filled in by the user or the cache
507 // - If withCredentials has been set to false (e.g. by Qt WebKit for a cross-origin XMLHttpRequest) then
508 // we need to bail out if authentication is required.
509 if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
510 // Reset authenticator so the next request on that channel does not get messed up
511 auth = nullptr;
512 if (isProxy)
513 channels[i].proxyAuthenticator = QAuthenticator();
514 else
515 channels[i].authenticator = QAuthenticator();
516
517 // authentication is cancelled, send the current contents to the user.
518 emit reply->headerChanged();
519 emit reply->readyRead();
520 QNetworkReply::NetworkError errorCode =
521 isProxy
522 ? QNetworkReply::ProxyAuthenticationRequiredError
523 : QNetworkReply::AuthenticationRequiredError;
524 reply->d_func()->errorString = errorDetail(errorCode, socket);
525 emit reply->finishedWithError(errorCode, detail: reply->d_func()->errorString);
526 // ### at this point the reply could be deleted
527 return true;
528 }
529 //resend the request
530 resend = true;
531 return true;
532 }
533 return false;
534}
535
536// Used by the HTTP1 code-path
537QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket,
538 QHttpNetworkReply *reply)
539{
540 ParseRedirectResult result = parseRedirectResponse(reply);
541 if (result.errorCode != QNetworkReply::NoError) {
542 emitReplyError(socket, reply, errorCode: result.errorCode);
543 return {};
544 }
545 return std::move(result.redirectUrl);
546}
547
548QHttpNetworkConnectionPrivate::ParseRedirectResult
549QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
550{
551 if (!reply->request().isFollowRedirects())
552 return {.redirectUrl: {}, .errorCode: QNetworkReply::NoError};
553
554 QUrl redirectUrl;
555 const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
556 for (const QNetworkReply::RawHeaderPair &header : fields) {
557 if (header.first.compare(c: "location", cs: Qt::CaseInsensitive) == 0) {
558 redirectUrl = QUrl::fromEncoded(url: header.second);
559 break;
560 }
561 }
562
563 // If the location url is invalid/empty, we return ProtocolUnknownError
564 if (!redirectUrl.isValid())
565 return {.redirectUrl: {}, .errorCode: QNetworkReply::ProtocolUnknownError};
566
567 // Check if we have exceeded max redirects allowed
568 if (reply->request().redirectCount() <= 0)
569 return {.redirectUrl: {}, .errorCode: QNetworkReply::TooManyRedirectsError};
570
571 // Resolve the URL if it's relative
572 if (redirectUrl.isRelative())
573 redirectUrl = reply->request().url().resolved(relative: redirectUrl);
574
575 // Check redirect url protocol
576 const QUrl priorUrl(reply->request().url());
577 if (redirectUrl.scheme() == QLatin1String("http") || redirectUrl.scheme() == QLatin1String("https")) {
578 switch (reply->request().redirectPolicy()) {
579 case QNetworkRequest::NoLessSafeRedirectPolicy:
580 // Here we could handle https->http redirects as InsecureProtocolError.
581 // However, if HSTS is enabled and redirectUrl.host() is a known STS
582 // host, then we'll replace its scheme and this won't downgrade protocol,
583 // after all. We cannot access QNAM's STS cache from here, so delegate
584 // this check to QNetworkReplyHttpImpl.
585 break;
586 case QNetworkRequest::SameOriginRedirectPolicy:
587 if (priorUrl.host() != redirectUrl.host()
588 || priorUrl.scheme() != redirectUrl.scheme()
589 || priorUrl.port() != redirectUrl.port()) {
590 return {.redirectUrl: {}, .errorCode: QNetworkReply::InsecureRedirectError};
591 }
592 break;
593 case QNetworkRequest::UserVerifiedRedirectPolicy:
594 break;
595 default:
596 Q_ASSERT(!"Unexpected redirect policy");
597 }
598 } else {
599 return {.redirectUrl: {}, .errorCode: QNetworkReply::ProtocolUnknownError};
600 }
601 return {.redirectUrl: std::move(redirectUrl), .errorCode: QNetworkReply::NoError};
602}
603
604void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
605{
606 Q_ASSERT(socket);
607
608 int i = indexOf(socket);
609
610 // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
611 if (channels[i].authMethod != QAuthenticatorPrivate::None) {
612 if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField(name: "Authorization").isEmpty()) || channels[i].lastStatus == 401) {
613 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth&: channels[i].authenticator);
614 if (priv && priv->method != QAuthenticatorPrivate::None) {
615 QByteArray response = priv->calculateResponse(method: request.methodName(), path: request.uri(throughProxy: false), host: request.url().host());
616 request.setHeaderField(name: "Authorization", data: response);
617 channels[i].authenticationCredentialsSent = true;
618 }
619 }
620 }
621
622#if QT_CONFIG(networkproxy)
623 // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
624 if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
625 if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
626 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth&: channels[i].proxyAuthenticator);
627 if (priv && priv->method != QAuthenticatorPrivate::None) {
628 QByteArray response = priv->calculateResponse(method: request.methodName(), path: request.uri(throughProxy: false), host: networkProxy.hostName());
629 request.setHeaderField(name: "Proxy-Authorization", data: response);
630 channels[i].proxyCredentialsSent = true;
631 }
632 }
633 }
634#endif // QT_CONFIG(networkproxy)
635}
636
637QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
638{
639 Q_Q(QHttpNetworkConnection);
640
641 // The reply component of the pair is created initially.
642 QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
643 reply->setRequest(request);
644 reply->d_func()->connection = q;
645 reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
646 HttpMessagePair pair = qMakePair(x: request, y: reply);
647
648 if (request.isPreConnect())
649 preConnectRequests++;
650
651 if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP
652 || (!encrypt && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 && !channels[0].switchedToHttp2)) {
653 switch (request.priority()) {
654 case QHttpNetworkRequest::HighPriority:
655 highPriorityQueue.prepend(t: pair);
656 break;
657 case QHttpNetworkRequest::NormalPriority:
658 case QHttpNetworkRequest::LowPriority:
659 lowPriorityQueue.prepend(t: pair);
660 break;
661 }
662 }
663 else { // SPDY, HTTP/2 ('h2' mode)
664 if (!pair.second->d_func()->requestIsPrepared)
665 prepareRequest(messagePair&: pair);
666 channels[0].spdyRequestsToSend.insert(akey: request.priority(), avalue: pair);
667 }
668
669#ifndef Q_OS_WINRT
670 // For Happy Eyeballs the networkLayerState is set to Unknown
671 // until we have started the first connection attempt. So no
672 // request will be started until we know if IPv4 or IPv6
673 // should be used.
674 if (networkLayerState == Unknown || networkLayerState == HostLookupPending) {
675 startHostInfoLookup();
676 } else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
677#else // !Q_OS_WINRT
678 {
679 // Skip the host lookup part for winrt. Host lookup and proxy handling are done by Windows
680 // internally and networkLayerPreference is ignored on this platform. Instead of refactoring
681 // the whole approach we just pretend that everything important is known here.
682 networkLayerState = IPv4;
683#endif
684 // this used to be called via invokeMethod and a QueuedConnection
685 // It is the only place _q_startNextRequest is called directly without going
686 // through the event loop using a QueuedConnection.
687 // This is dangerous because of recursion that might occur when emitting
688 // signals as DirectConnection from this code path. Therefore all signal
689 // emissions that can come out from this code path need to
690 // be QueuedConnection.
691 // We are currently trying to fine-tune this.
692 _q_startNextRequest();
693 }
694 return reply;
695}
696
697void QHttpNetworkConnectionPrivate::fillHttp2Queue()
698{
699 for (auto &pair : highPriorityQueue) {
700 if (!pair.second->d_func()->requestIsPrepared)
701 prepareRequest(messagePair&: pair);
702 channels[0].spdyRequestsToSend.insert(akey: QHttpNetworkRequest::HighPriority, avalue: pair);
703 }
704
705 highPriorityQueue.clear();
706
707 for (auto &pair : lowPriorityQueue) {
708 if (!pair.second->d_func()->requestIsPrepared)
709 prepareRequest(messagePair&: pair);
710 channels[0].spdyRequestsToSend.insert(akey: pair.first.priority(), avalue: pair);
711 }
712
713 lowPriorityQueue.clear();
714}
715
716void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
717{
718 Q_Q(QHttpNetworkConnection);
719
720 QHttpNetworkRequest request = pair.first;
721 switch (request.priority()) {
722 case QHttpNetworkRequest::HighPriority:
723 highPriorityQueue.prepend(t: pair);
724 break;
725 case QHttpNetworkRequest::NormalPriority:
726 case QHttpNetworkRequest::LowPriority:
727 lowPriorityQueue.prepend(t: pair);
728 break;
729 }
730
731 QMetaObject::invokeMethod(obj: q, member: "_q_startNextRequest", type: Qt::QueuedConnection);
732}
733
734bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
735{
736 int i = 0;
737 if (socket)
738 i = indexOf(socket);
739
740 if (!highPriorityQueue.isEmpty()) {
741 // remove from queue before sendRequest! else we might pipeline the same request again
742 HttpMessagePair messagePair = highPriorityQueue.takeLast();
743 if (!messagePair.second->d_func()->requestIsPrepared)
744 prepareRequest(messagePair);
745 updateChannel(i, messagePair);
746 return true;
747 }
748
749 if (!lowPriorityQueue.isEmpty()) {
750 // remove from queue before sendRequest! else we might pipeline the same request again
751 HttpMessagePair messagePair = lowPriorityQueue.takeLast();
752 if (!messagePair.second->d_func()->requestIsPrepared)
753 prepareRequest(messagePair);
754 updateChannel(i, messagePair);
755 return true;
756 }
757 return false;
758}
759
760void QHttpNetworkConnectionPrivate::updateChannel(int i, const HttpMessagePair &messagePair)
761{
762 channels[i].request = messagePair.first;
763 channels[i].reply = messagePair.second;
764 // Now that reply is assigned a channel, correct reply to channel association
765 // previously set in queueRequest.
766 channels[i].reply->d_func()->connectionChannel = &channels[i];
767}
768
769QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest() const
770{
771 if (!highPriorityQueue.isEmpty())
772 return highPriorityQueue.last().first;
773 if (!lowPriorityQueue.isEmpty())
774 return lowPriorityQueue.last().first;
775 return QHttpNetworkRequest();
776}
777
778// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
779void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
780{
781 // return fast if there is nothing to pipeline
782 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
783 return;
784
785 int i = indexOf(socket);
786
787 // return fast if there was no reply right now processed
788 if (channels[i].reply == nullptr)
789 return;
790
791 if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
792 return;
793 }
794
795 if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
796 return;
797
798 // the current request that is in must already support pipelining
799 if (!channels[i].request.isPipeliningAllowed())
800 return;
801
802 // the current request must be a idempotent (right now we only check GET)
803 if (channels[i].request.operation() != QHttpNetworkRequest::Get)
804 return;
805
806 // check if socket is connected
807 if (socket->state() != QAbstractSocket::ConnectedState)
808 return;
809
810 // check for resendCurrent
811 if (channels[i].resendCurrent)
812 return;
813
814 // we do not like authentication stuff
815 // ### make sure to be OK with this in later releases
816 if (!channels[i].authenticator.isNull()
817 && (!channels[i].authenticator.user().isEmpty()
818 || !channels[i].authenticator.password().isEmpty()))
819 return;
820 if (!channels[i].proxyAuthenticator.isNull()
821 && (!channels[i].proxyAuthenticator.user().isEmpty()
822 || !channels[i].proxyAuthenticator.password().isEmpty()))
823 return;
824
825 // must be in ReadingState or WaitingState
826 if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
827 || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
828 return;
829
830 int lengthBefore;
831 while (!highPriorityQueue.isEmpty()) {
832 lengthBefore = channels[i].alreadyPipelinedRequests.length();
833 fillPipeline(queue&: highPriorityQueue, channel&: channels[i]);
834
835 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
836 channels[i].pipelineFlush();
837 return;
838 }
839
840 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
841 break; // did not process anything, now do the low prio queue
842 }
843
844 while (!lowPriorityQueue.isEmpty()) {
845 lengthBefore = channels[i].alreadyPipelinedRequests.length();
846 fillPipeline(queue&: lowPriorityQueue, channel&: channels[i]);
847
848 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
849 channels[i].pipelineFlush();
850 return;
851 }
852
853 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
854 break; // did not process anything
855 }
856
857
858 channels[i].pipelineFlush();
859}
860
861// returns true when the processing of a queue has been done
862bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
863{
864 if (queue.isEmpty())
865 return true;
866
867 for (int i = queue.count() - 1; i >= 0; --i) {
868 HttpMessagePair messagePair = queue.at(i);
869 const QHttpNetworkRequest &request = messagePair.first;
870
871 // we currently do not support pipelining if HTTP authentication is used
872 if (!request.url().userInfo().isEmpty())
873 continue;
874
875 // take only GET requests
876 if (request.operation() != QHttpNetworkRequest::Get)
877 continue;
878
879 if (!request.isPipeliningAllowed())
880 continue;
881
882 // remove it from the queue
883 queue.takeAt(i);
884 // we modify the queue we iterate over here, but since we return from the function
885 // afterwards this is fine.
886
887 // actually send it
888 if (!messagePair.second->d_func()->requestIsPrepared)
889 prepareRequest(messagePair);
890 channel.pipelineInto(pair&: messagePair);
891
892 // return false because we processed something and need to process again
893 return false;
894 }
895
896 // return true, the queue has been processed and not changed
897 return true;
898}
899
900
901QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail)
902{
903 QString errorString;
904 switch (errorCode) {
905 case QNetworkReply::HostNotFoundError:
906 if (socket)
907 errorString = QCoreApplication::translate(context: "QHttp", key: "Host %1 not found").arg(a: socket->peerName());
908 else
909 errorString = QCoreApplication::translate(context: "QHttp", key: "Host %1 not found").arg(a: hostName);
910 break;
911 case QNetworkReply::ConnectionRefusedError:
912 errorString = QCoreApplication::translate(context: "QHttp", key: "Connection refused");
913 break;
914 case QNetworkReply::RemoteHostClosedError:
915 errorString = QCoreApplication::translate(context: "QHttp", key: "Connection closed");
916 break;
917 case QNetworkReply::TimeoutError:
918 errorString = QCoreApplication::translate(context: "QAbstractSocket", key: "Socket operation timed out");
919 break;
920 case QNetworkReply::ProxyAuthenticationRequiredError:
921 errorString = QCoreApplication::translate(context: "QHttp", key: "Proxy requires authentication");
922 break;
923 case QNetworkReply::AuthenticationRequiredError:
924 errorString = QCoreApplication::translate(context: "QHttp", key: "Host requires authentication");
925 break;
926 case QNetworkReply::ProtocolFailure:
927 errorString = QCoreApplication::translate(context: "QHttp", key: "Data corrupted");
928 break;
929 case QNetworkReply::ProtocolUnknownError:
930 errorString = QCoreApplication::translate(context: "QHttp", key: "Unknown protocol specified");
931 break;
932 case QNetworkReply::SslHandshakeFailedError:
933 errorString = QCoreApplication::translate(context: "QHttp", key: "SSL handshake failed");
934 break;
935 case QNetworkReply::TooManyRedirectsError:
936 errorString = QCoreApplication::translate(context: "QHttp", key: "Too many redirects");
937 break;
938 case QNetworkReply::InsecureRedirectError:
939 errorString = QCoreApplication::translate(context: "QHttp", key: "Insecure redirect");
940 break;
941 default:
942 // all other errors are treated as QNetworkReply::UnknownNetworkError
943 errorString = extraDetail;
944 break;
945 }
946 return errorString;
947}
948
949// this is called from the destructor of QHttpNetworkReply. It is called when
950// the reply was finished correctly or when it was aborted.
951void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
952{
953 Q_Q(QHttpNetworkConnection);
954
955 // check if the reply is currently being processed or it is pipelined in
956 for (int i = 0; i < activeChannelCount; ++i) {
957 // is the reply associated the currently processing of this channel?
958 if (channels[i].reply == reply) {
959 channels[i].reply = nullptr;
960 if (channels[i].protocolHandler)
961 channels[i].protocolHandler->setReply(nullptr);
962 channels[i].request = QHttpNetworkRequest();
963 channels[i].resendCurrent = false;
964
965 if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
966 // the reply had to be prematurely removed, e.g. it was not finished
967 // therefore we have to requeue the already pipelined requests.
968 channels[i].requeueCurrentlyPipelinedRequests();
969 }
970
971 // if HTTP mandates we should close
972 // or the reply is not finished yet, e.g. it was aborted
973 // we have to close that connection
974 if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished()) {
975 if (reply->isAborted()) {
976 channels[i].abort();
977 } else {
978 channels[i].close();
979 }
980 }
981
982 QMetaObject::invokeMethod(obj: q, member: "_q_startNextRequest", type: Qt::QueuedConnection);
983 return;
984 }
985
986 // is the reply inside the pipeline of this channel already?
987 for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
988 if (channels[i].alreadyPipelinedRequests.at(i: j).second == reply) {
989 // Remove that HttpMessagePair
990 channels[i].alreadyPipelinedRequests.removeAt(i: j);
991
992 channels[i].requeueCurrentlyPipelinedRequests();
993
994 // Since some requests had already been pipelined, but we removed
995 // one and re-queued the others
996 // we must force a connection close after the request that is
997 // currently in processing has been finished.
998 if (channels[i].reply)
999 channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
1000
1001 QMetaObject::invokeMethod(obj: q, member: "_q_startNextRequest", type: Qt::QueuedConnection);
1002 return;
1003 }
1004 }
1005#ifndef QT_NO_SSL
1006 // is the reply inside the SPDY pipeline of this channel already?
1007 QMultiMap<int, HttpMessagePair>::iterator it = channels[i].spdyRequestsToSend.begin();
1008 QMultiMap<int, HttpMessagePair>::iterator end = channels[i].spdyRequestsToSend.end();
1009 for (; it != end; ++it) {
1010 if (it.value().second == reply) {
1011 channels[i].spdyRequestsToSend.remove(akey: it.key());
1012
1013 QMetaObject::invokeMethod(obj: q, member: "_q_startNextRequest", type: Qt::QueuedConnection);
1014 return;
1015 }
1016 }
1017#endif
1018 }
1019 // remove from the high priority queue
1020 if (!highPriorityQueue.isEmpty()) {
1021 for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
1022 HttpMessagePair messagePair = highPriorityQueue.at(i: j);
1023 if (messagePair.second == reply) {
1024 highPriorityQueue.removeAt(i: j);
1025 QMetaObject::invokeMethod(obj: q, member: "_q_startNextRequest", type: Qt::QueuedConnection);
1026 return;
1027 }
1028 }
1029 }
1030 // remove from the low priority queue
1031 if (!lowPriorityQueue.isEmpty()) {
1032 for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
1033 HttpMessagePair messagePair = lowPriorityQueue.at(i: j);
1034 if (messagePair.second == reply) {
1035 lowPriorityQueue.removeAt(i: j);
1036 QMetaObject::invokeMethod(obj: q, member: "_q_startNextRequest", type: Qt::QueuedConnection);
1037 return;
1038 }
1039 }
1040 }
1041}
1042
1043
1044
1045// This function must be called from the event loop. The only
1046// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
1047// although it is called _q_startNextRequest, it will actually start multiple requests when possible
1048void QHttpNetworkConnectionPrivate::_q_startNextRequest()
1049{
1050 // If there is no network layer state decided we should not start any new requests.
1051 if (networkLayerState == Unknown || networkLayerState == HostLookupPending || networkLayerState == IPv4or6)
1052 return;
1053
1054 // If the QHttpNetworkConnection is currently paused then bail out immediately
1055 if (state == PausedState)
1056 return;
1057
1058 //resend the necessary ones.
1059 for (int i = 0; i < activeChannelCount; ++i) {
1060 if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
1061 channels[i].resendCurrent = false;
1062
1063 // if this is not possible, error will be emitted and connection terminated
1064 if (!channels[i].resetUploadData())
1065 continue;
1066 channels[i].sendRequest();
1067 }
1068 }
1069
1070 // dequeue new ones
1071
1072 switch (connectionType) {
1073 case QHttpNetworkConnection::ConnectionTypeHTTP: {
1074 // return fast if there is nothing to do
1075 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
1076 return;
1077
1078 // try to get a free AND connected socket
1079 for (int i = 0; i < activeChannelCount; ++i) {
1080 if (channels[i].socket) {
1081 if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
1082 if (dequeueRequest(socket: channels[i].socket))
1083 channels[i].sendRequest();
1084 }
1085 }
1086 }
1087 break;
1088 }
1089 case QHttpNetworkConnection::ConnectionTypeHTTP2Direct:
1090 case QHttpNetworkConnection::ConnectionTypeHTTP2:
1091 case QHttpNetworkConnection::ConnectionTypeSPDY: {
1092 if (channels[0].spdyRequestsToSend.isEmpty() && !channels[0].reply
1093 && highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) {
1094 return;
1095 }
1096
1097 if (networkLayerState == IPv4)
1098 channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1099 else if (networkLayerState == IPv6)
1100 channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1101 channels[0].ensureConnection();
1102 if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
1103 && !channels[0].pendingEncrypt && channels[0].spdyRequestsToSend.size())
1104 channels[0].sendRequest();
1105 break;
1106 }
1107 }
1108
1109 // try to push more into all sockets
1110 // ### FIXME we should move this to the beginning of the function
1111 // as soon as QtWebkit is properly using the pipelining
1112 // (e.g. not for XMLHttpRequest or the first page load)
1113 // ### FIXME we should also divide the requests more even
1114 // on the connected sockets
1115 //tryToFillPipeline(socket);
1116 // return fast if there is nothing to pipeline
1117 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
1118 return;
1119 for (int i = 0; i < activeChannelCount; i++)
1120 if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState)
1121 fillPipeline(socket: channels[i].socket);
1122
1123 // If there is not already any connected channels we need to connect a new one.
1124 // We do not pair the channel with the request until we know if it is
1125 // connected or not. This is to reuse connected channels before we connect new once.
1126 int queuedRequests = highPriorityQueue.count() + lowPriorityQueue.count();
1127
1128 // in case we have in-flight preconnect requests and normal requests,
1129 // we only need one socket for each (preconnect, normal request) pair
1130 int neededOpenChannels = queuedRequests;
1131 if (preConnectRequests > 0) {
1132 int normalRequests = queuedRequests - preConnectRequests;
1133 neededOpenChannels = qMax(a: normalRequests, b: preConnectRequests);
1134 }
1135
1136 if (neededOpenChannels <= 0)
1137 return;
1138
1139 QQueue<int> channelsToConnect;
1140
1141 // use previously used channels first
1142 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1143 if (!channels[i].socket)
1144 continue;
1145
1146 if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
1147 || (channels[i].socket->state() == QAbstractSocket::HostLookupState)
1148 || channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState"
1149 neededOpenChannels--;
1150 continue;
1151 }
1152
1153 if (!channels[i].reply && !channels[i].isSocketBusy()
1154 && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
1155 channelsToConnect.enqueue(t: i);
1156 neededOpenChannels--;
1157 }
1158 }
1159
1160 // use other channels
1161 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1162 if (channels[i].socket)
1163 continue;
1164
1165 channelsToConnect.enqueue(t: i);
1166 neededOpenChannels--;
1167 }
1168
1169 while (!channelsToConnect.isEmpty()) {
1170 const int channel = channelsToConnect.dequeue();
1171
1172 if (networkLayerState == IPv4)
1173 channels[channel].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1174 else if (networkLayerState == IPv6)
1175 channels[channel].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1176
1177 channels[channel].ensureConnection();
1178 }
1179}
1180
1181
1182void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
1183{
1184 for (int i = 0 ; i < activeChannelCount; ++i) {
1185 if (channels[i].reply == reply) {
1186 // emulate a readyRead() from the socket
1187 QMetaObject::invokeMethod(obj: &channels[i], member: "_q_readyRead", type: Qt::QueuedConnection);
1188 return;
1189 }
1190 }
1191}
1192
1193
1194
1195// The first time we start the connection is used we do not know if we
1196// should use IPv4 or IPv6. So we start a hostlookup to figure this out.
1197// Later when we do the connection the socket will not need to do another
1198// lookup as then the hostinfo will already be in the cache.
1199void QHttpNetworkConnectionPrivate::startHostInfoLookup()
1200{
1201 networkLayerState = HostLookupPending;
1202
1203 // check if we already now can decide if this is IPv4 or IPv6
1204 QString lookupHost = hostName;
1205#ifndef QT_NO_NETWORKPROXY
1206 if (networkProxy.capabilities() & QNetworkProxy::HostNameLookupCapability) {
1207 lookupHost = networkProxy.hostName();
1208 } else if (channels[0].proxy.capabilities() & QNetworkProxy::HostNameLookupCapability) {
1209 lookupHost = channels[0].proxy.hostName();
1210 }
1211#endif
1212 QHostAddress temp;
1213 if (temp.setAddress(lookupHost)) {
1214 const QAbstractSocket::NetworkLayerProtocol protocol = temp.protocol();
1215 if (protocol == QAbstractSocket::IPv4Protocol) {
1216 networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1217 QMetaObject::invokeMethod(obj: this->q_func(), member: "_q_startNextRequest", type: Qt::QueuedConnection);
1218 return;
1219 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1220 networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1221 QMetaObject::invokeMethod(obj: this->q_func(), member: "_q_startNextRequest", type: Qt::QueuedConnection);
1222 return;
1223 }
1224 } else {
1225 int hostLookupId;
1226 bool immediateResultValid = false;
1227 QHostInfo hostInfo = qt_qhostinfo_lookup(name: lookupHost,
1228 receiver: this->q_func(),
1229 SLOT(_q_hostLookupFinished(QHostInfo)),
1230 valid: &immediateResultValid,
1231 id: &hostLookupId);
1232 if (immediateResultValid) {
1233 _q_hostLookupFinished(info: hostInfo);
1234 }
1235 }
1236}
1237
1238
1239void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
1240{
1241 bool bIpv4 = false;
1242 bool bIpv6 = false;
1243 bool foundAddress = false;
1244 if (networkLayerState == IPv4 || networkLayerState == IPv6 || networkLayerState == IPv4or6)
1245 return;
1246
1247 const auto addresses = info.addresses();
1248 for (const QHostAddress &address : addresses) {
1249 const QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
1250 if (protocol == QAbstractSocket::IPv4Protocol) {
1251 if (!foundAddress) {
1252 foundAddress = true;
1253 delayIpv4 = false;
1254 }
1255 bIpv4 = true;
1256 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1257 if (!foundAddress) {
1258 foundAddress = true;
1259 delayIpv4 = true;
1260 }
1261 bIpv6 = true;
1262 }
1263 }
1264
1265 if (bIpv4 && bIpv6)
1266 startNetworkLayerStateLookup();
1267 else if (bIpv4) {
1268 networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1269 QMetaObject::invokeMethod(obj: this->q_func(), member: "_q_startNextRequest", type: Qt::QueuedConnection);
1270 } else if (bIpv6) {
1271 networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1272 QMetaObject::invokeMethod(obj: this->q_func(), member: "_q_startNextRequest", type: Qt::QueuedConnection);
1273 } else {
1274 if (dequeueRequest(socket: channels[0].socket)) {
1275 emitReplyError(socket: channels[0].socket, reply: channels[0].reply, errorCode: QNetworkReply::HostNotFoundError);
1276 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1277 } else if (connectionType == QHttpNetworkConnection::ConnectionTypeSPDY
1278 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
1279 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1280 for (const HttpMessagePair &spdyPair : qAsConst(t&: channels[0].spdyRequestsToSend)) {
1281 // emit error for all replies
1282 QHttpNetworkReply *currentReply = spdyPair.second;
1283 Q_ASSERT(currentReply);
1284 emitReplyError(socket: channels[0].socket, reply: currentReply, errorCode: QNetworkReply::HostNotFoundError);
1285 }
1286 } else {
1287 // Should not happen: we start a host lookup before sending a request,
1288 // so it's natural to have requests either in SPDY/HTTP/2 queue,
1289 // or in low/high priority queues.
1290 qWarning(msg: "QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
1291 " could not de-queue request, failed to report HostNotFoundError");
1292 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1293 }
1294 }
1295}
1296
1297
1298// This will be used if the host lookup found both and Ipv4 and
1299// Ipv6 address. Then we will start up two connections and pick
1300// the network layer of the one that finish first. The second
1301// connection will then be disconnected.
1302void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
1303{
1304 if (activeChannelCount > 1) {
1305 // At this time all channels should be unconnected.
1306 Q_ASSERT(!channels[0].isSocketBusy());
1307 Q_ASSERT(!channels[1].isSocketBusy());
1308
1309 networkLayerState = IPv4or6;
1310
1311 channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1312 channels[1].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1313
1314 int timeout = 300;
1315#ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section
1316 if (networkSession) {
1317 const QNetworkConfiguration::BearerType bearerType = networkSession->configuration().bearerType();
1318 if (bearerType == QNetworkConfiguration::Bearer2G)
1319 timeout = 800;
1320 else if (bearerType == QNetworkConfiguration::BearerCDMA2000)
1321 timeout = 500;
1322 else if (bearerType == QNetworkConfiguration::BearerWCDMA)
1323 timeout = 500;
1324 else if (bearerType == QNetworkConfiguration::BearerHSPA)
1325 timeout = 400;
1326 }
1327#endif
1328 delayedConnectionTimer.start(msec: timeout);
1329 if (delayIpv4)
1330 channels[1].ensureConnection();
1331 else
1332 channels[0].ensureConnection();
1333 } else {
1334 networkLayerState = IPv4or6;
1335 channels[0].networkLayerPreference = QAbstractSocket::AnyIPProtocol;
1336 channels[0].ensureConnection();
1337 }
1338}
1339
1340void QHttpNetworkConnectionPrivate::networkLayerDetected(QAbstractSocket::NetworkLayerProtocol protocol)
1341{
1342 for (int i = 0 ; i < activeChannelCount; ++i) {
1343 if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
1344 channels[i].close();
1345 }
1346 }
1347}
1348
1349void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
1350{
1351 if (delayIpv4)
1352 channels[0].ensureConnection();
1353 else
1354 channels[1].ensureConnection();
1355}
1356
1357#ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section
1358QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
1359 QHttpNetworkConnection::ConnectionType connectionType,
1360 QObject *parent, QSharedPointer<QNetworkSession> networkSession)
1361 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt, connectionType)), parent)
1362{
1363 Q_D(QHttpNetworkConnection);
1364 d->networkSession = std::move(networkSession);
1365 d->init();
1366 if (QNetworkStatusMonitor::isEnabled()) {
1367 connect(sender: &d->connectionMonitor, signal: &QNetworkConnectionMonitor::reachabilityChanged,
1368 receiver: this, slot: &QHttpNetworkConnection::onlineStateChanged, type: Qt::QueuedConnection);
1369 }
1370}
1371
1372QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
1373 quint16 port, bool encrypt, QObject *parent,
1374 QSharedPointer<QNetworkSession> networkSession,
1375 QHttpNetworkConnection::ConnectionType connectionType)
1376 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
1377 connectionType)), parent)
1378{
1379 Q_D(QHttpNetworkConnection);
1380 d->networkSession = std::move(networkSession);
1381 d->init();
1382 if (QNetworkStatusMonitor::isEnabled()) {
1383 connect(sender: &d->connectionMonitor, signal: &QNetworkConnectionMonitor::reachabilityChanged,
1384 receiver: this, slot: &QHttpNetworkConnection::onlineStateChanged, type: Qt::QueuedConnection);
1385 }
1386}
1387#else
1388QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
1389 QHttpNetworkConnection::ConnectionType connectionType, QObject *parent)
1390 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt , connectionType)), parent)
1391{
1392 Q_D(QHttpNetworkConnection);
1393 d->init();
1394 if (QNetworkStatusMonitor::isEnabled()) {
1395 connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
1396 this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
1397 }
1398}
1399
1400QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
1401 quint16 port, bool encrypt, QObject *parent,
1402 QHttpNetworkConnection::ConnectionType connectionType)
1403 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
1404 connectionType)), parent)
1405{
1406 Q_D(QHttpNetworkConnection);
1407 d->init();
1408 if (QNetworkStatusMonitor::isEnabled()) {
1409 connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
1410 this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
1411 }
1412}
1413#endif // QT_NO_BEARERMANAGEMENT
1414
1415QHttpNetworkConnection::~QHttpNetworkConnection()
1416{
1417}
1418
1419QString QHttpNetworkConnection::hostName() const
1420{
1421 Q_D(const QHttpNetworkConnection);
1422 return d->hostName;
1423}
1424
1425quint16 QHttpNetworkConnection::port() const
1426{
1427 Q_D(const QHttpNetworkConnection);
1428 return d->port;
1429}
1430
1431QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
1432{
1433 Q_D(QHttpNetworkConnection);
1434 return d->queueRequest(request);
1435}
1436
1437void QHttpNetworkConnection::fillHttp2Queue()
1438{
1439 Q_D(QHttpNetworkConnection);
1440 d->fillHttp2Queue();
1441}
1442
1443bool QHttpNetworkConnection::isSsl() const
1444{
1445 Q_D(const QHttpNetworkConnection);
1446 return d->encrypt;
1447}
1448
1449QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const
1450{
1451 return d_func()->channels;
1452}
1453
1454#ifndef QT_NO_NETWORKPROXY
1455void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
1456{
1457 Q_D(QHttpNetworkConnection);
1458 d->networkProxy = networkProxy;
1459 // update the authenticator
1460 if (!d->networkProxy.user().isEmpty()) {
1461 for (int i = 0; i < d->channelCount; ++i) {
1462 d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
1463 d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
1464 }
1465 }
1466}
1467
1468QNetworkProxy QHttpNetworkConnection::cacheProxy() const
1469{
1470 Q_D(const QHttpNetworkConnection);
1471 return d->networkProxy;
1472}
1473
1474void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
1475{
1476 Q_D(QHttpNetworkConnection);
1477 for (int i = 0; i < d->channelCount; ++i)
1478 d->channels[i].setProxy(networkProxy);
1479}
1480
1481QNetworkProxy QHttpNetworkConnection::transparentProxy() const
1482{
1483 Q_D(const QHttpNetworkConnection);
1484 return d->channels[0].proxy;
1485}
1486#endif
1487
1488QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType()
1489{
1490 Q_D(QHttpNetworkConnection);
1491 return d->connectionType;
1492}
1493
1494void QHttpNetworkConnection::setConnectionType(ConnectionType type)
1495{
1496 Q_D(QHttpNetworkConnection);
1497 d->connectionType = type;
1498}
1499
1500QHttp2Configuration QHttpNetworkConnection::http2Parameters() const
1501{
1502 Q_D(const QHttpNetworkConnection);
1503 return d->http2Parameters;
1504}
1505
1506void QHttpNetworkConnection::setHttp2Parameters(const QHttp2Configuration &params)
1507{
1508 Q_D(QHttpNetworkConnection);
1509 d->http2Parameters = params;
1510}
1511
1512// SSL support below
1513#ifndef QT_NO_SSL
1514void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
1515{
1516 Q_D(QHttpNetworkConnection);
1517 if (!d->encrypt)
1518 return;
1519
1520 // set the config on all channels
1521 for (int i = 0; i < d->activeChannelCount; ++i)
1522 d->channels[i].setSslConfiguration(config);
1523}
1524
1525QSharedPointer<QSslContext> QHttpNetworkConnection::sslContext()
1526{
1527 Q_D(QHttpNetworkConnection);
1528 return d->sslContext;
1529}
1530
1531void QHttpNetworkConnection::setSslContext(QSharedPointer<QSslContext> context)
1532{
1533 Q_D(QHttpNetworkConnection);
1534 d->sslContext = std::move(context);
1535}
1536
1537void QHttpNetworkConnection::ignoreSslErrors(int channel)
1538{
1539 Q_D(QHttpNetworkConnection);
1540 if (!d->encrypt)
1541 return;
1542
1543 if (channel == -1) { // ignore for all channels
1544 // We need to ignore for all channels, even the ones that are not in use just in case they
1545 // will be in the future.
1546 for (int i = 0; i < d->channelCount; ++i) {
1547 d->channels[i].ignoreSslErrors();
1548 }
1549
1550 } else {
1551 d->channels[channel].ignoreSslErrors();
1552 }
1553}
1554
1555void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
1556{
1557 Q_D(QHttpNetworkConnection);
1558 if (!d->encrypt)
1559 return;
1560
1561 if (channel == -1) { // ignore for all channels
1562 // We need to ignore for all channels, even the ones that are not in use just in case they
1563 // will be in the future.
1564 for (int i = 0; i < d->channelCount; ++i) {
1565 d->channels[i].ignoreSslErrors(errors);
1566 }
1567
1568 } else {
1569 d->channels[channel].ignoreSslErrors(errors);
1570 }
1571}
1572
1573#endif //QT_NO_SSL
1574
1575void QHttpNetworkConnection::preConnectFinished()
1576{
1577 d_func()->preConnectRequests--;
1578}
1579
1580QString QHttpNetworkConnection::peerVerifyName() const
1581{
1582 Q_D(const QHttpNetworkConnection);
1583 return d->peerVerifyName;
1584}
1585
1586void QHttpNetworkConnection::setPeerVerifyName(const QString &peerName)
1587{
1588 Q_D(QHttpNetworkConnection);
1589 d->peerVerifyName = peerName;
1590}
1591
1592void QHttpNetworkConnection::onlineStateChanged(bool isOnline)
1593{
1594 Q_D(QHttpNetworkConnection);
1595
1596 if (isOnline) {
1597 // If we did not have any 'isOffline' previously - well, good
1598 // to know, we are 'online' apparently.
1599 return;
1600 }
1601
1602 for (int i = 0; i < d->activeChannelCount; i++) {
1603 auto &channel = d->channels[i];
1604 channel.emitFinishedWithError(error: QNetworkReply::TemporaryNetworkFailureError, message: "Temporary network failure.");
1605 channel.close();
1606 }
1607
1608 // We don't care, this connection is broken from our POV.
1609 d->connectionMonitor.stopMonitoring();
1610}
1611
1612#ifndef QT_NO_NETWORKPROXY
1613// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
1614// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
1615// e.g. it is for SOCKS proxies which require authentication.
1616void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
1617{
1618 // Also pause the connection because socket notifiers may fire while an user
1619 // dialog is displaying
1620 pauseConnection();
1621 QHttpNetworkReply *reply;
1622 if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
1623 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
1624#if QT_CONFIG(ssl)
1625 || connectionType == QHttpNetworkConnection::ConnectionTypeSPDY
1626#endif
1627 ) {
1628
1629 // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
1630 // but that does not matter because the signal will ultimately be emitted
1631 // by the QNetworkAccessManager.
1632 Q_ASSERT(chan->spdyRequestsToSend.count() > 0);
1633 reply = chan->spdyRequestsToSend.cbegin().value().second;
1634 } else { // HTTP
1635 reply = chan->reply;
1636 }
1637
1638 Q_ASSERT(reply);
1639 emit reply->proxyAuthenticationRequired(proxy, authenticator: auth);
1640 resumeConnection();
1641 int i = indexOf(socket: chan->socket);
1642 copyCredentials(fromChannel: i, auth, isProxy: true);
1643}
1644#endif
1645
1646
1647QT_END_NAMESPACE
1648
1649#include "moc_qhttpnetworkconnection_p.cpp"
1650

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