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

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