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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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