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