Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qhttpnetworkconnection.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <private/qabstractsocket_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
35
36using namespace Qt::StringLiterals;
37
38// The pipeline length. So there will be 4 requests in flight.
40// Only re-fill the pipeline if there's defaultRePipelineLength slots free in the pipeline.
41// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
43
52
54 quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
56 : hostName(hostName),
57 port(port),
58 encrypt(encrypt),
59 isLocalSocket(isLocalSocket),
60 activeChannelCount(getPreferredActiveChannelCount(type, 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
70 // We allocate all 6 channels even if it's an HTTP/2-enabled
71 // connection: in case the protocol negotiation via NPN/ALPN fails,
72 // we will have normally working HTTP/1.1.
74}
75
76
77
79{
80 for (int i = 0; i < channelCount; ++i) {
81 if (channels[i].socket) {
82 QObject::disconnect(channels[i].socket, nullptr, &channels[i], nullptr);
84 delete channels[i].socket;
85 }
86 }
87 delete []channels;
88}
89
101
103{
105
106 // Disable all socket notifiers
107 for (int i = 0; i < activeChannelCount; i++) {
108 if (auto *absSocket = qobject_cast<QAbstractSocket *>(channels[i].socket)) {
109#ifndef QT_NO_SSL
110 if (encrypt)
111 QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(absSocket));
112 else
113#endif
115 } else if (qobject_cast<QLocalSocket *>(channels[i].socket)) {
116 // @todo how would we do this?
117#if 0 // @todo Enable this when there is a debug category for this
118 qDebug() << "Should pause socket but there is no way to do it for local sockets";
119#endif
120 }
121 }
122}
123
125{
127 // Enable all socket notifiers
128 for (int i = 0; i < activeChannelCount; i++) {
129 if (auto *absSocket = qobject_cast<QAbstractSocket *>(channels[i].socket)) {
130#ifndef QT_NO_SSL
131 if (encrypt)
133 else
134#endif
136
137 // Resume pending upload if needed
139 QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
140 } else if (qobject_cast<QLocalSocket *>(channels[i].socket)) {
141#if 0 // @todo Enable this when there is a debug category for this
142 qDebug() << "Should resume socket but there is no way to do it for local sockets";
143#endif
144 }
145 }
146
147 // queue _q_startNextRequest
148 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
149}
150
152{
153 for (int i = 0; i < activeChannelCount; ++i)
154 if (channels[i].socket == socket)
155 return i;
156
157 qFatal("Called with unknown socket object.");
158 return 0;
159}
160
161// If the connection is in the HostLookupPendening state channel errors should not always be
162// emitted. This function will check the status of the connection channels if we
163// have not decided the networkLayerState and will return true if the channel error
164// should be emitted by the channel.
166{
168
169 bool emitError = true;
170 int i = indexOf(socket);
171 int otherSocket = (i == 0 ? 1 : 0);
172
173 // If the IPv4 connection still isn't started we need to start it now.
176 channels[otherSocket].ensureConnection();
177 }
178
182 channels[0].close();
183 emitError = true;
184 } else {
186 if (channels[otherSocket].isSocketBusy() && (channels[otherSocket].state != QHttpNetworkConnectionChannel::ClosingState)) {
187 // this was the first socket to fail.
188 channels[i].close();
189 emitError = false;
190 }
191 else {
192 // Both connection attempts has failed.
194 channels[i].close();
195 emitError = true;
196 }
197 } else {
200 // First connection worked so this is the second one to complete and it failed.
201 channels[i].close();
202 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
203 emitError = false;
204 }
206 qWarning("We got a connection error when networkLayerState is Unknown");
207 }
208 }
209 return emitError;
210}
211
212
214{
215 return reply.d_func()->responseData.byteAmount();
216}
217
219{
220 return reply.d_func()->responseData.sizeNextBlock();
221}
222
224{
226 if (systemLocale == "C"_L1)
227 return "en,*"_ba;
228 systemLocale.replace('_'_L1, '-'_L1);
229 if (systemLocale.startsWith("en-"_L1))
230 return (systemLocale + ",*"_L1).toLatin1();
231 return (systemLocale + ",en,*"_L1).toLatin1();
232}
233
235{
236 QHttpNetworkRequest &request = messagePair.first;
237 QHttpNetworkReply *reply = messagePair.second;
238
239 // add missing fields for the request
241#ifndef Q_OS_WASM
242 // check if Content-Length is provided
243 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
244 if (uploadByteDevice) {
245 const qint64 contentLength = request.contentLength();
246 const qint64 uploadDeviceSize = uploadByteDevice->size();
247 if (contentLength != -1 && uploadDeviceSize != -1) {
248 // Both values known: use the smaller one.
249 if (uploadDeviceSize < contentLength)
250 request.setContentLength(uploadDeviceSize);
251 } else if (contentLength == -1 && uploadDeviceSize != -1) {
252 // content length not supplied by user, but the upload device knows it
253 request.setContentLength(uploadDeviceSize);
254 } else if (contentLength != -1 && uploadDeviceSize == -1) {
255 // everything OK, the user supplied us the contentLength
256 } else if (Q_UNLIKELY(contentLength == -1 && uploadDeviceSize == -1)) {
257 qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
258 }
259 }
260#endif
261 // set the Connection/Proxy-Connection: Keep-Alive headers
262#ifndef QT_NO_NETWORKPROXY
264 value = request.headerField("proxy-connection");
265 if (value.isEmpty())
266 request.setHeaderField("Proxy-Connection", "Keep-Alive");
267 } else {
268#endif
269 value = request.headerField("connection");
270 if (value.isEmpty())
271 request.setHeaderField("Connection", "Keep-Alive");
272#ifndef QT_NO_NETWORKPROXY
273 }
274#endif
275
276 // If the request had a accept-encoding set, we better not mess
277 // with it. If it was not set, we announce that we understand gzip
278 // and remember this fact in request.d->autoDecompress so that
279 // we can later decompress the HTTP reply if it has such an
280 // encoding.
281 value = request.headerField("accept-encoding");
282 if (value.isEmpty()) {
283#ifndef QT_NO_COMPRESS
284 const static QByteArray acceptedEncoding = QDecompressHelper::acceptedEncoding().join(", ");
285 request.setHeaderField("Accept-Encoding", acceptedEncoding);
286 request.d->autoDecompress = true;
287#else
288 // if zlib is not available set this to false always
289 request.d->autoDecompress = false;
290#endif
291 }
292
293 // some websites mandate an accept-language header and fail
294 // if it is not sent. This is a problem with the website and
295 // not with us, but we work around this by setting
296 // one always.
297 value = request.headerField("accept-language");
298 if (value.isEmpty())
299 request.setHeaderField("Accept-Language", makeAcceptLanguage());
300
301 // set the User Agent
302 value = request.headerField("user-agent");
303 if (value.isEmpty())
304 request.setHeaderField("User-Agent", "Mozilla/5.0");
305 // set the host
306 value = request.headerField("host");
307 if (value.isEmpty()) {
309 QByteArray host;
310 if (add.setAddress(hostName)) {
311 if (add.protocol() == QAbstractSocket::IPv6Protocol)
312 host = (u'[' + hostName + u']').toLatin1(); //format the ipv6 in the standard way
313 else
314 host = hostName.toLatin1();
315
316 } else {
317 host = QUrl::toAce(hostName);
318 }
319
320 int port = request.url().port();
321 if (port != -1) {
322 host += ':';
323 host += QByteArray::number(port);
324 }
325
326 request.prependHeaderField("Host", host);
327 }
328
329 reply->d_func()->requestIsPrepared = true;
330}
331
332
333
334
338{
340
341 int i = 0;
342 if (socket)
343 i = indexOf(socket);
344
345 if (reply) {
346 // this error matters only to this reply
347 reply->d_func()->errorString = errorDetail(errorCode, socket);
348 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
349 // remove the corrupt data if any
350 reply->d_func()->eraseData();
351
352 // Clean the channel
353 channels[i].close();
354 channels[i].reply = nullptr;
355 if (channels[i].protocolHandler)
356 channels[i].protocolHandler->setReply(nullptr);
358 if (socket)
360
361 // send the next request
362 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
363 }
364}
365
366void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
367{
368 Q_ASSERT(auth);
369
370 // NTLM and Negotiate do multi-phase authentication.
371 // Copying credentialsbetween authenticators would mess things up.
372 if (fromChannel >= 0) {
374 if (priv
375 && (priv->method == QAuthenticatorPrivate::Ntlm
376 || priv->method == QAuthenticatorPrivate::Negotiate)) {
377 return;
378 }
379 }
380
381 // select another channel
382 QAuthenticator* otherAuth = nullptr;
383 for (int i = 0; i < activeChannelCount; ++i) {
384 if (i == fromChannel)
385 continue;
386 if (isProxy)
387 otherAuth = &channels[i].proxyAuthenticator;
388 else
389 otherAuth = &channels[i].authenticator;
390 // if the credentials are different, copy them
391 if (otherAuth->user().compare(auth->user()))
392 otherAuth->setUser(auth->user());
393 if (otherAuth->password().compare(auth->password()))
394 otherAuth->setPassword(auth->password());
395 }
396}
397
398
399// handles the authentication for one channel and eventually re-starts the other channels
401 bool isProxy, bool &resend)
402{
405
406 resend = false;
407 //create the response header to be used with QAuthenticatorPrivate.
408 const auto headers = reply->header();
409
410 // Check that any of the proposed authenticate methods are supported
411 const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
412 const QByteArrayList &authenticationMethods = reply->d_func()->headerFieldValues(header);
413 const bool isSupported = std::any_of(authenticationMethods.begin(), authenticationMethods.end(),
415 if (isSupported) {
416 int i = indexOf(socket);
417 //Use a single authenticator for all domains. ### change later to use domain/realm
418 QAuthenticator *auth = isProxy ? &channels[i].proxyAuthenticator
420 //proceed with the authentication.
421 if (auth->isNull())
422 auth->detach();
424 priv->parseHttpResponse(headers, isProxy, reply->url().host());
425 // Update method in case it changed
426 if (priv->method == QAuthenticatorPrivate::None)
427 return false;
428
429 if (priv->phase == QAuthenticatorPrivate::Done ||
431 && (priv->method == QAuthenticatorPrivate::Ntlm
432 || priv->method == QAuthenticatorPrivate::Negotiate))) {
435
437 if (!isProxy) {
438 if (channels[i].authenticationCredentialsSent) {
439 auth->detach();
441 priv->hasFailed = true;
444 }
445 emit reply->authenticationRequired(reply->request(), auth);
446#ifndef QT_NO_NETWORKPROXY
447 } else {
448 if (channels[i].proxyCredentialsSent) {
449 auth->detach();
451 priv->hasFailed = true;
454 }
455 emit reply->proxyAuthenticationRequired(networkProxy, auth);
456#endif
457 }
459
460 if (priv->phase != QAuthenticatorPrivate::Done) {
461 // send any pending requests
462 copyCredentials(i, auth, isProxy);
463 }
464 } else if (priv->phase == QAuthenticatorPrivate::Start) {
465 // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
466 // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
467 // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
468 emit reply->cacheCredentials(reply->request(), auth);
469 }
470 // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
471 // then nothing was filled in by the user or the cache
472 // - If withCredentials has been set to false (e.g. by Qt WebKit for a cross-origin XMLHttpRequest) then
473 // we need to bail out if authentication is required.
474 if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
475 // Reset authenticator so the next request on that channel does not get messed up
476 auth = nullptr;
477 if (isProxy)
479 else
481
482 // authentication is cancelled, send the current contents to the user.
483 emit reply->headerChanged();
486 isProxy
489 reply->d_func()->errorString = errorDetail(errorCode, socket);
490 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
491 // ### at this point the reply could be deleted
492 return true;
493 }
494 //resend the request
495 resend = true;
496 return true;
497 }
498 return false;
499}
500
501// Used by the HTTP1 code-path
512
515{
516 if (!reply->request().isFollowRedirects())
517 return {{}, QNetworkReply::NoError};
518
519 QUrl redirectUrl;
520 const QHttpHeaders fields = reply->header();
521 if (const auto h = fields.values(QHttpHeaders::WellKnownHeader::Location); !h.empty()) {
522 redirectUrl = QUrl::fromEncoded(h.first());
523 }
524
525 // If the location url is invalid/empty, we return ProtocolUnknownError
526 if (!redirectUrl.isValid())
528
529 // Check if we have exceeded max redirects allowed
530 if (reply->request().redirectCount() <= 0)
532
533 // Resolve the URL if it's relative
534 if (redirectUrl.isRelative())
535 redirectUrl = reply->request().url().resolved(redirectUrl);
536
537 // Check redirect url protocol
538 const QUrl priorUrl(reply->request().url());
539 const QString targetUrlScheme = redirectUrl.scheme();
540 if (targetUrlScheme == "http"_L1 || targetUrlScheme == "https"_L1
541 || targetUrlScheme.startsWith("unix"_L1)) {
542 switch (reply->request().redirectPolicy()) {
544 // Here we could handle https->http redirects as InsecureProtocolError.
545 // However, if HSTS is enabled and redirectUrl.host() is a known STS
546 // host, then we'll replace its scheme and this won't downgrade protocol,
547 // after all. We cannot access QNAM's STS cache from here, so delegate
548 // this check to QNetworkReplyHttpImpl.
549 break;
551 if (priorUrl.host() != redirectUrl.host()
552 || priorUrl.scheme() != targetUrlScheme
553 || priorUrl.port() != redirectUrl.port()) {
555 }
556 break;
558 break;
559 default:
560 Q_ASSERT(!"Unexpected redirect policy");
561 }
562 } else {
564 }
565 return {std::move(redirectUrl), QNetworkReply::NoError};
566}
567
569{
571
573
574 QAuthenticator *authenticator = &channel.authenticator;
576 // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
577 if (priv && priv->method != QAuthenticatorPrivate::None) {
578 const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
580 const bool authNeeded = channel.lastStatus == 401;
581 const bool ntlmNegoOk = ntlmNego && authNeeded
583 || !channel.authenticationCredentialsSent);
584 const bool otherOk =
585 !ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty());
586 if (ntlmNegoOk || otherOk) {
587 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
588 request.url().host());
589 request.setHeaderField("Authorization", response);
590 channel.authenticationCredentialsSent = true;
591 }
592 }
593
594#if QT_CONFIG(networkproxy)
595 authenticator = &channel.proxyAuthenticator;
596 priv = QAuthenticatorPrivate::getPrivate(*authenticator);
597 // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
598 if (priv && priv->method != QAuthenticatorPrivate::None) {
599 const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
601 const bool proxyAuthNeeded = channel.lastStatus == 407;
602 const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded
603 && (priv->phase != QAuthenticatorPrivate::Done || !channel.proxyCredentialsSent);
604 const bool otherOk = !ntlmNego;
605 if (ntlmNegoOk || otherOk) {
606 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
608 request.setHeaderField("Proxy-Authorization", response);
609 channel.proxyCredentialsSent = true;
610 }
611 }
612#endif // QT_CONFIG(networkproxy)
613}
614
616{
618
619 // The reply component of the pair is created initially.
622 reply->d_func()->connection = q;
623 reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
625
626 if (request.isPreConnect())
628
631 switch (request.priority()) {
634 break;
638 break;
639 }
640 }
641 else { // HTTP/2 ('h2' mode)
642 if (!pair.second->d_func()->requestIsPrepared)
643 prepareRequest(pair);
645 }
646
647 // For Happy Eyeballs the networkLayerState is set to Unknown
648 // until we have started the first connection attempt. So no
649 // request will be started until we know if IPv4 or IPv6
650 // should be used.
653 } else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
654 // this used to be called via invokeMethod and a QueuedConnection
655 // It is the only place _q_startNextRequest is called directly without going
656 // through the event loop using a QueuedConnection.
657 // This is dangerous because of recursion that might occur when emitting
658 // signals as DirectConnection from this code path. Therefore all signal
659 // emissions that can come out from this code path need to
660 // be QueuedConnection.
661 // We are currently trying to fine-tune this.
663 }
664 return reply;
665}
666
668{
669 for (auto &pair : highPriorityQueue) {
670 if (!pair.second->d_func()->requestIsPrepared)
671 prepareRequest(pair);
673 }
674
676
677 for (auto &pair : lowPriorityQueue) {
678 if (!pair.second->d_func()->requestIsPrepared)
679 prepareRequest(pair);
680 channels[0].h2RequestsToSend.insert(pair.first.priority(), pair);
681 }
682
684}
685
703
705{
706 int i = 0;
707 if (socket)
708 i = indexOf(socket);
709
710 if (!highPriorityQueue.isEmpty()) {
711 // remove from queue before sendRequest! else we might pipeline the same request again
713 if (!messagePair.second->d_func()->requestIsPrepared)
714 prepareRequest(messagePair);
715 updateChannel(i, messagePair);
716 return true;
717 }
718
719 if (!lowPriorityQueue.isEmpty()) {
720 // remove from queue before sendRequest! else we might pipeline the same request again
722 if (!messagePair.second->d_func()->requestIsPrepared)
723 prepareRequest(messagePair);
724 updateChannel(i, messagePair);
725 return true;
726 }
727 return false;
728}
729
731{
732 channels[i].request = messagePair.first;
733 channels[i].reply = messagePair.second;
734 // Now that reply is assigned a channel, correct reply to channel association
735 // previously set in queueRequest.
736 channels[i].reply->d_func()->connectionChannel = &channels[i];
737}
738
747
749{
751 return highPriorityQueue.last().second;
753 return lowPriorityQueue.last().second;
754 return nullptr;
755}
756
757// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
759{
760 // return fast if there is nothing to pipeline
762 return;
763
764 int i = indexOf(socket);
765
766 // return fast if there was no reply right now processed
767 if (channels[i].reply == nullptr)
768 return;
769
770 if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.size() >= defaultRePipelineLength)) {
771 return;
772 }
773
775 return;
776
777 // the current request that is in must already support pipelining
778 if (!channels[i].request.isPipeliningAllowed())
779 return;
780
781 // the current request must be a idempotent (right now we only check GET)
782 if (channels[i].request.operation() != QHttpNetworkRequest::Get)
783 return;
784
785 // check if socket is connected
787 return;
788
789 // check for resendCurrent
790 if (channels[i].resendCurrent)
791 return;
792
793 // we do not like authentication stuff
794 // ### make sure to be OK with this in later releases
795 if (!channels[i].authenticator.isNull()
798 return;
799 if (!channels[i].proxyAuthenticator.isNull()
802 return;
803
804 // must be in ReadingState or WaitingState
807 return;
808
809 int lengthBefore;
810 while (!highPriorityQueue.isEmpty()) {
811 lengthBefore = channels[i].alreadyPipelinedRequests.size();
813
814 if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
816 return;
817 }
818
819 if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
820 break; // did not process anything, now do the low prio queue
821 }
822
823 while (!lowPriorityQueue.isEmpty()) {
824 lengthBefore = channels[i].alreadyPipelinedRequests.size();
826
827 if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
829 return;
830 }
831
832 if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
833 break; // did not process anything
834 }
835
836
838}
839
840// returns true when the processing of a queue has been done
842{
843 if (queue.isEmpty())
844 return true;
845
846 for (int i = queue.size() - 1; i >= 0; --i) {
847 HttpMessagePair messagePair = queue.at(i);
848 const QHttpNetworkRequest &request = messagePair.first;
849
850 // we currently do not support pipelining if HTTP authentication is used
851 if (!request.url().userInfo().isEmpty())
852 continue;
853
854 // take only GET requests
855 if (request.operation() != QHttpNetworkRequest::Get)
856 continue;
857
858 if (!request.isPipeliningAllowed())
859 continue;
860
861 // remove it from the queue
862 queue.takeAt(i);
863 // we modify the queue we iterate over here, but since we return from the function
864 // afterwards this is fine.
865
866 // actually send it
867 if (!messagePair.second->d_func()->requestIsPrepared)
868 prepareRequest(messagePair);
869 channel.pipelineInto(messagePair);
870
871 // return false because we processed something and need to process again
872 return false;
873 }
874
875 // return true, the queue has been processed and not changed
876 return true;
877}
878
879
881{
882 QString errorString;
883 switch (errorCode) {
886 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(peerName);
887 break;
888 }
890 errorString = QCoreApplication::translate("QHttp", "Connection refused");
891 break;
893 errorString = QCoreApplication::translate("QHttp", "Connection closed");
894 break;
896 errorString = QCoreApplication::translate("QAbstractSocket", "Socket operation timed out");
897 break;
899 errorString = QCoreApplication::translate("QHttp", "Proxy requires authentication");
900 break;
902 errorString = QCoreApplication::translate("QHttp", "Host requires authentication");
903 break;
905 errorString = QCoreApplication::translate("QHttp", "Data corrupted");
906 break;
908 errorString = QCoreApplication::translate("QHttp", "Unknown protocol specified");
909 break;
911 errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
912 if (socket)
913 errorString += ": "_L1 + socket->errorString();
914 break;
916 errorString = QCoreApplication::translate("QHttp", "Too many redirects");
917 break;
919 errorString = QCoreApplication::translate("QHttp", "Insecure redirect");
920 break;
921 default:
922 // all other errors are treated as QNetworkReply::UnknownNetworkError
923 errorString = extraDetail;
924 break;
925 }
926 return errorString;
927}
928
929// this is called from the destructor of QHttpNetworkReply. It is called when
930// the reply was finished correctly or when it was aborted.
932{
934
935 // check if the reply is currently being processed or it is pipelined in
936 for (int i = 0; i < activeChannelCount; ++i) {
937 // is the reply associated the currently processing of this channel?
938 if (channels[i].reply == reply) {
939 channels[i].reply = nullptr;
940 if (channels[i].protocolHandler)
941 channels[i].protocolHandler->setReply(nullptr);
943 channels[i].resendCurrent = false;
944
946 // the reply had to be prematurely removed, e.g. it was not finished
947 // therefore we have to requeue the already pipelined requests.
949 }
950
951 // if HTTP mandates we should close
952 // or the reply is not finished yet, e.g. it was aborted
953 // we have to close that connection
954 if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished()) {
955 if (reply->isAborted()) {
956 channels[i].abort();
957 } else {
958 channels[i].close();
959 }
960 }
961
962 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
963 return;
964 }
965
966 // is the reply inside the pipeline of this channel already?
967 for (int j = 0; j < channels[i].alreadyPipelinedRequests.size(); j++) {
968 if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
969 // Remove that HttpMessagePair
971
973
974 // Since some requests had already been pipelined, but we removed
975 // one and re-queued the others
976 // we must force a connection close after the request that is
977 // currently in processing has been finished.
978 if (channels[i].reply)
979 channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
980
981 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
982 return;
983 }
984 }
985 // is the reply inside the H2 pipeline of this channel already?
986 const auto foundReply = [reply](const HttpMessagePair &pair) {
987 return pair.second == reply;
988 };
989 auto &seq = channels[i].h2RequestsToSend;
990 const auto end = seq.cend();
991 auto it = std::find_if(seq.cbegin(), end, foundReply);
992 if (it != end) {
993 seq.erase(it);
994 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
995 return;
996 }
997 }
998 // remove from the high priority queue
999 if (!highPriorityQueue.isEmpty()) {
1000 for (int j = highPriorityQueue.size() - 1; j >= 0; --j) {
1001 HttpMessagePair messagePair = highPriorityQueue.at(j);
1002 if (messagePair.second == reply) {
1004 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1005 return;
1006 }
1007 }
1008 }
1009 // remove from the low priority queue
1010 if (!lowPriorityQueue.isEmpty()) {
1011 for (int j = lowPriorityQueue.size() - 1; j >= 0; --j) {
1012 HttpMessagePair messagePair = lowPriorityQueue.at(j);
1013 if (messagePair.second == reply) {
1015 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1016 return;
1017 }
1018 }
1019 }
1020}
1021
1022
1023
1024// This function must be called from the event loop. The only
1025// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
1026// although it is called _q_startNextRequest, it will actually start multiple requests when possible
1028{
1029 // If there is no network layer state decided we should not start any new requests.
1031 return;
1032
1033 // If the QHttpNetworkConnection is currently paused then bail out immediately
1034 if (state == PausedState)
1035 return;
1036
1037 //resend the necessary ones.
1038 for (int i = 0; i < activeChannelCount; ++i) {
1040 if (!channels[i].socket
1042 if (!channels[i].ensureConnection())
1043 continue;
1044 }
1045 channels[i].resendCurrent = false;
1046
1047 // if this is not possible, error will be emitted and connection terminated
1048 if (!channels[i].resetUploadData())
1049 continue;
1051 }
1052 }
1053
1054 // dequeue new ones
1055
1056 switch (connectionType) {
1058 // return fast if there is nothing to do
1060 return;
1061
1062 // try to get a free AND connected socket
1063 for (int i = 0; i < activeChannelCount; ++i) {
1064 if (channels[i].socket) {
1065 if (!channels[i].reply && !channels[i].isSocketBusy()
1070 }
1071 }
1072 }
1073 break;
1074 }
1077 if (channels[0].h2RequestsToSend.isEmpty() && !channels[0].reply
1079 return;
1080 }
1081
1082 if (networkLayerState == IPv4)
1084 else if (networkLayerState == IPv6)
1087 if (auto *s = channels[0].socket; s
1089 && !channels[0].pendingEncrypt) {
1090 if (channels[0].h2RequestsToSend.size()) {
1091 channels[0].sendRequest();
1092 } else if (!channels[0].reply && !channels[0].switchedToHttp2) {
1093 // This covers an edge-case where we're already connected and the "connected"
1094 // signal was already sent, but we didn't have any request available at the time,
1095 // so it was missed. As such we need to dequeue a request and send it now that we
1096 // have one.
1098 channels[0].sendRequest();
1099 }
1100 }
1101 break;
1102 }
1103 }
1104
1105 // try to push more into all sockets
1106 // ### FIXME we should move this to the beginning of the function
1107 // as soon as QtWebkit is properly using the pipelining
1108 // (e.g. not for XMLHttpRequest or the first page load)
1109 // ### FIXME we should also divide the requests more even
1110 // on the connected sockets
1111 //tryToFillPipeline(socket);
1112 // return fast if there is nothing to pipeline
1114 return;
1115 for (int i = 0; i < activeChannelCount; i++) {
1116 if (channels[i].socket
1120 }
1121 }
1122
1123 // If there is not already any connected channels we need to connect a new one.
1124 // We do not pair the channel with the request until we know if it is
1125 // connected or not. This is to reuse connected channels before we connect new once.
1126 int queuedRequests = highPriorityQueue.size() + lowPriorityQueue.size();
1127
1128 // in case we have in-flight preconnect requests and normal requests,
1129 // we only need one socket for each (preconnect, normal request) pair
1130 int neededOpenChannels = queuedRequests;
1131 if (preConnectRequests > 0) {
1132 int normalRequests = queuedRequests - preConnectRequests;
1133 neededOpenChannels = qMax(normalRequests, preConnectRequests);
1134 }
1135
1136 if (neededOpenChannels <= 0)
1137 return;
1138
1139 QVarLengthArray<int> channelsToConnect;
1140
1141 // use previously used channels first
1142 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1143 if (!channels[i].socket)
1144 continue;
1145
1147 if ((QSocketAbstraction::socketState(channels[i].socket) == State::ConnectingState)
1148 || (QSocketAbstraction::socketState(channels[i].socket) == State::HostLookupState)
1149 || channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState"
1150 neededOpenChannels--;
1151 continue;
1152 }
1153
1154 if (!channels[i].reply && !channels[i].isSocketBusy()
1155 && (QSocketAbstraction::socketState(channels[i].socket) == State::UnconnectedState)) {
1156 channelsToConnect.push_back(i);
1157 neededOpenChannels--;
1158 }
1159 }
1160
1161 // use other channels
1162 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1163 if (channels[i].socket)
1164 continue;
1165
1166 channelsToConnect.push_back(i);
1167 neededOpenChannels--;
1168 }
1169
1170 auto channelToConnectSpan = QSpan{channelsToConnect};
1171 while (!channelToConnectSpan.isEmpty()) {
1172 const int channel = channelToConnectSpan.front();
1173 channelToConnectSpan = channelToConnectSpan.sliced(1);
1174
1175 if (networkLayerState == IPv4)
1177 else if (networkLayerState == IPv6)
1179
1181 }
1182}
1183
1184
1186{
1187 for (int i = 0 ; i < activeChannelCount; ++i) {
1188 if (channels[i].reply == reply) {
1189 // emulate a readyRead() from the socket
1191 return;
1192 }
1193 }
1194}
1195
1196
1197
1198// The first time we start the connection is used we do not know if we
1199// should use IPv4 or IPv6. So we start a hostlookup to figure this out.
1200// Later when we do the connection the socket will not need to do another
1201// lookup as then the hostinfo will already be in the cache.
1203{
1205
1206 // check if we already now can decide if this is IPv4 or IPv6
1207 QString lookupHost = hostName;
1208#ifndef QT_NO_NETWORKPROXY
1210 lookupHost = networkProxy.hostName();
1212 lookupHost = channels[0].proxy.hostName();
1213 }
1214#endif
1215 QHostAddress temp;
1216 if (temp.setAddress(lookupHost)) {
1217 const QAbstractSocket::NetworkLayerProtocol protocol = temp.protocol();
1218 if (protocol == QAbstractSocket::IPv4Protocol) {
1220 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1221 return;
1222 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1224 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1225 return;
1226 }
1227 } else {
1228 int hostLookupId;
1229 bool immediateResultValid = false;
1230 QHostInfo hostInfo = qt_qhostinfo_lookup(lookupHost,
1231 this->q_func(),
1233 &immediateResultValid,
1234 &hostLookupId);
1235 if (immediateResultValid) {
1236 _q_hostLookupFinished(hostInfo);
1237 }
1238 }
1239}
1240
1241
1243{
1244 bool bIpv4 = false;
1245 bool bIpv6 = false;
1246 bool foundAddress = false;
1248 return;
1249
1250 const auto addresses = info.addresses();
1251 for (const QHostAddress &address : addresses) {
1252 const QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
1253 if (protocol == QAbstractSocket::IPv4Protocol) {
1254 if (!foundAddress) {
1255 foundAddress = true;
1256 delayIpv4 = false;
1257 }
1258 bIpv4 = true;
1259 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1260 if (!foundAddress) {
1261 foundAddress = true;
1262 delayIpv4 = true;
1263 }
1264 bIpv6 = true;
1265 }
1266 }
1267
1268 if (bIpv4 && bIpv6)
1270 else if (bIpv4) {
1272 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1273 } else if (bIpv6) {
1275 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1276 } else {
1277 auto lookupError = QNetworkReply::HostNotFoundError;
1278#ifndef QT_NO_NETWORKPROXY
1279 // if the proxy can lookup hostnames, all hostname lookups except for the lookup of the
1280 // proxy hostname are delegated to the proxy.
1281 auto proxyCapabilities = networkProxy.capabilities() | channels[0].proxy.capabilities();
1282 if (proxyCapabilities & QNetworkProxy::HostNameLookupCapability)
1284#endif
1285 if (dequeueRequest(channels[0].socket)) {
1286 emitReplyError(channels[0].socket, channels[0].reply, lookupError);
1290 for (const HttpMessagePair &h2Pair : std::as_const(channels[0].h2RequestsToSend)) {
1291 // emit error for all replies
1292 QHttpNetworkReply *currentReply = h2Pair.second;
1293 Q_ASSERT(currentReply);
1294 emitReplyError(channels[0].socket, currentReply, lookupError);
1295 }
1296 } else {
1297 // We can end up here if a request has been aborted or otherwise failed (e.g. timeout)
1298 // before the host lookup was finished.
1299 qDebug("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
1300 " could not de-queue request, failed to report HostNotFoundError");
1302 }
1303 }
1304}
1305
1306
1307// This will be used if the host lookup found both and Ipv4 and
1308// Ipv6 address. Then we will start up two connections and pick
1309// the network layer of the one that finish first. The second
1310// connection will then be disconnected.
1312{
1313 if (activeChannelCount > 1) {
1314 // At this time all channels should be unconnected.
1315 Q_ASSERT(!channels[0].isSocketBusy());
1316 Q_ASSERT(!channels[1].isSocketBusy());
1317
1319
1322
1323 int timeout = 300;
1325 if (delayIpv4)
1327 else
1329 } else {
1333 }
1334}
1335
1337{
1338 for (int i = 0 ; i < activeChannelCount; ++i) {
1339 if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
1340 channels[i].close();
1341 }
1342 }
1343}
1344
1352
1354 quint16 port, bool encrypt, bool isLocalSocket, QObject *parent,
1356 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt, isLocalSocket,
1357 connectionType)), parent)
1358{
1360 d->init();
1364 }
1365}
1366
1370
1372{
1373 Q_D(const QHttpNetworkConnection);
1374 return d->hostName;
1375}
1376
1378{
1379 Q_D(const QHttpNetworkConnection);
1380 return d->port;
1381}
1382
1388
1390{
1392 d->fillHttp2Queue();
1393}
1394
1396{
1397 Q_D(const QHttpNetworkConnection);
1398 return d->encrypt;
1399}
1400
1402{
1403 return d_func()->channels;
1404}
1405
1406#ifndef QT_NO_NETWORKPROXY
1408{
1410 d->networkProxy = networkProxy;
1411 // update the authenticator
1412 if (!d->networkProxy.user().isEmpty()) {
1413 for (int i = 0; i < d->channelCount; ++i) {
1414 d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
1415 d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
1416 }
1417 }
1418}
1419
1421{
1422 Q_D(const QHttpNetworkConnection);
1423 return d->networkProxy;
1424}
1425
1427{
1429 for (int i = 0; i < d->channelCount; ++i)
1430 d->channels[i].setProxy(networkProxy);
1431}
1432
1434{
1435 Q_D(const QHttpNetworkConnection);
1436 return d->channels[0].proxy;
1437}
1438#endif
1439
1441{
1442 Q_D(const QHttpNetworkConnection);
1443 return d->connectionType;
1444}
1445
1451
1453{
1454 Q_D(const QHttpNetworkConnection);
1455 return d->http2Parameters;
1456}
1457
1463
1464// SSL support below
1465#ifndef QT_NO_SSL
1467{
1469 if (!d->encrypt)
1470 return;
1471
1472 // set the config on all channels
1473 for (int i = 0; i < d->activeChannelCount; ++i)
1474 d->channels[i].setSslConfiguration(config);
1475}
1476
1477std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext() const
1478{
1479 Q_D(const QHttpNetworkConnection);
1480 return d->sslContext;
1481}
1482
1483void QHttpNetworkConnection::setSslContext(std::shared_ptr<QSslContext> context)
1484{
1486 d->sslContext = std::move(context);
1487}
1488
1490{
1492 if (!d->encrypt)
1493 return;
1494
1495 if (channel == -1) { // ignore for all channels
1496 // We need to ignore for all channels, even the ones that are not in use just in case they
1497 // will be in the future.
1498 for (int i = 0; i < d->channelCount; ++i) {
1499 d->channels[i].ignoreSslErrors();
1500 }
1501
1502 } else {
1503 d->channels[channel].ignoreSslErrors();
1504 }
1505}
1506
1507void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
1508{
1510 if (!d->encrypt)
1511 return;
1512
1513 if (channel == -1) { // ignore for all channels
1514 // We need to ignore for all channels, even the ones that are not in use just in case they
1515 // will be in the future.
1516 for (int i = 0; i < d->channelCount; ++i) {
1517 d->channels[i].ignoreSslErrors(errors);
1518 }
1519
1520 } else {
1521 d->channels[channel].ignoreSslErrors(errors);
1522 }
1523}
1524
1525#endif //QT_NO_SSL
1526
1528{
1529 d_func()->preConnectRequests--;
1530}
1531
1533{
1534 Q_D(const QHttpNetworkConnection);
1535 return d->peerVerifyName;
1536}
1537
1539{
1541 d->peerVerifyName = peerName;
1542}
1543
1545{
1547
1548 if (isOnline) {
1549 // If we did not have any 'isOffline' previously - well, good
1550 // to know, we are 'online' apparently.
1551 return;
1552 }
1553
1554 for (int i = 0; i < d->activeChannelCount; i++) {
1555 auto &channel = d->channels[i];
1556 channel.emitFinishedWithError(QNetworkReply::TemporaryNetworkFailureError, "Temporary network failure.");
1557 channel.close();
1558 }
1559
1560 // We don't care, this connection is broken from our POV.
1561 d->connectionMonitor.stopMonitoring();
1562}
1563
1564#ifndef QT_NO_NETWORKPROXY
1565// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
1566// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
1567// e.g. it is for SOCKS proxies which require authentication.
1569{
1570 // Also pause the connection because socket notifiers may fire while an user
1571 // dialog is displaying
1575 && (chan->switchedToHttp2 || chan->h2RequestsToSend.size() > 0))
1577 // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
1578 // but that does not matter because the signal will ultimately be emitted
1579 // by the QNetworkAccessManager.
1580 Q_ASSERT(chan->h2RequestsToSend.size() > 0);
1581 reply = chan->h2RequestsToSend.cbegin().value().second;
1582 } else { // HTTP
1583 reply = chan->reply;
1584 }
1585
1586 Q_ASSERT(reply);
1587 emit reply->proxyAuthenticationRequired(proxy, auth);
1589 int i = indexOf(chan->socket);
1590 copyCredentials(i, auth, true);
1591}
1592#endif
1593
1594
1596
1597#include "moc_qhttpnetworkconnection_p.cpp"
IOBluetoothL2CAPChannel * channel
static void pauseSocketNotifiers(QAbstractSocket *)
static void resumeSocketNotifiers(QAbstractSocket *)
SocketState
This enum describes the different states in which a socket can be.
static constexpr auto IPv4Protocol
static constexpr auto AnyIPProtocol
static constexpr auto IPv6Protocol
\inmodule QtGui
static bool isMethodSupported(QByteArrayView method)
static QAuthenticatorPrivate * getPrivate(QAuthenticator &auth)
The QAuthenticator class provides an authentication object.
QString user() const
Returns the user used for authentication.
QString password() const
Returns the password used for authentication.
void setUser(const QString &user)
Sets the user used for authentication.
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
static QByteArrayList acceptedEncoding()
The QHostAddress class provides an IP address.
void setAddress(quint32 ip4Addr)
Set the IPv4 address specified by ip4Addr.
NetworkLayerProtocol protocol() const
Returns the network layer protocol of the host address.
The QHostInfo class provides static functions for host name lookups.
Definition qhostinfo.h:19
QList< QHostAddress > addresses() const
Returns the list of IP addresses associated with hostName().
The QHttp2Configuration class controls HTTP/2 parameters and settings.
Q_NETWORK_EXPORT QList< QByteArray > values(QAnyStringView name) const
Returns the values of header name in a list.
std::unique_ptr< QAbstractProtocolHandler > protocolHandler
QAbstractSocket::NetworkLayerProtocol networkLayerPreference
QMultiMap< int, HttpMessagePair > h2RequestsToSend
void setConnection(QHttpNetworkConnection *c)
QString errorDetail(QNetworkReply::NetworkError errorCode, QIODevice *socket, const QString &extraDetail=QString())
QHttpNetworkConnection::ConnectionType connectionType
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
bool shouldEmitChannelError(QIODevice *socket)
void createAuthorization(QIODevice *socket, QHttpNetworkRequest &request)
void emitReplyError(QIODevice *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode)
QHttpNetworkRequest predictNextRequest() const
void prepareRequest(HttpMessagePair &request)
QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, bool isLocalSocket, QHttpNetworkConnection::ConnectionType type)
void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator *auth)
NetworkLayerPreferenceState networkLayerState
void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
void networkLayerDetected(QAbstractSocket::NetworkLayerProtocol protocol)
void requeueRequest(const HttpMessagePair &pair)
QList< HttpMessagePair > lowPriorityQueue
void removeReply(QHttpNetworkReply *reply)
void _q_hostLookupFinished(const QHostInfo &info)
QHttpNetworkReply * predictNextRequestsReply() const
QList< HttpMessagePair > highPriorityQueue
void updateChannel(int i, const HttpMessagePair &messagePair)
bool handleAuthenticateChallenge(QIODevice *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend)
QHttpNetworkReply * queueRequest(const QHttpNetworkRequest &request)
QHttpNetworkConnectionChannel *const channels
void readMoreLater(QHttpNetworkReply *reply)
int indexOf(QIODevice *socket) const
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
static ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply)
QHttpNetworkReply * sendRequest(const QHttpNetworkRequest &request)
void onlineStateChanged(bool isOnline)
QHttp2Configuration http2Parameters() const
void setCacheProxy(const QNetworkProxy &networkProxy)
QHttpNetworkConnectionChannel * channels() const
void setPeerVerifyName(const QString &peerName)
void setConnectionType(ConnectionType type)
QNetworkProxy transparentProxy() const
ConnectionType connectionType() const
void setSslContext(std::shared_ptr< QSslContext > context)
std::shared_ptr< QSslContext > sslContext() const
void setTransparentProxy(const QNetworkProxy &networkProxy)
void setHttp2Parameters(const QHttp2Configuration &params)
void ignoreSslErrors(int channel=-1)
void setSslConfiguration(const QSslConfiguration &config)
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port=80, bool encrypt=false, bool isLocalSocket=false, QObject *parent=nullptr, ConnectionType connectionType=ConnectionTypeHTTP)
\inmodule QtCore \reentrant
Definition qiodevice.h:34
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
QString errorString() const
Returns a human-readable description of the last device error that occurred.
virtual void close()
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
T & last()
Definition qlist.h:648
void removeAt(qsizetype i)
Definition qlist.h:590
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
value_type takeLast()
Definition qlist.h:567
void prepend(rvalue_ref t)
Definition qlist.h:473
void clear()
Definition qlist.h:434
static QLocale system()
Returns a QLocale object initialized to the system locale.
Definition qlocale.cpp:2862
iterator insert(const Key &key, const T &value)
Definition qmap.h:1452
const_iterator cend() const
Definition qmap.h:1330
void reachabilityChanged(bool isOnline)
The QNetworkProxy class provides a network layer proxy.
void setUser(const QString &userName)
Sets the user name for proxy authentication to be user.
Capabilities capabilities() const
QNetworkProxy::ProxyType type() const
Returns the proxy type for this instance.
QString hostName() const
Returns the host name of the proxy host.
bool isFinished() const
QVariant header(QNetworkRequest::KnownHeaders header) const
Returns the value of the known header header, if that header was sent by the remote server.
void setRequest(const QNetworkRequest &request)
Sets the associated request for this object to be request.
NetworkError
Indicates all possible error conditions found during the processing of the request.
@ TemporaryNetworkFailureError
@ ProxyAuthenticationRequiredError
@ AuthenticationRequiredError
QNetworkRequest request() const
Returns the request that was posted for this reply.
QUrl url() const
Returns the URL of the content downloaded or uploaded.
Priority priority() const
QUrl url() const
Returns the URL this network request is referring to.
virtual qint64 size() const =0
Returns the size of the complete device or -1 if unknown.
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
Definition qspan.h:315
The QSslConfiguration class holds the configuration and state of an SSL connection.
static void resumeSocketNotifiers(QSslSocket *)
static void pauseSocketNotifiers(QSslSocket *)
The QSslSocket class provides an SSL encrypted socket for both clients and servers.
Definition qsslsocket.h:29
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void setSingleShot(bool singleShot)
Definition qtimer.cpp:552
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
Definition qtimer.cpp:167
void stop()
Stops the timer.
Definition qtimer.cpp:267
\inmodule QtCore
Definition qurl.h:94
QString userInfo(ComponentFormattingOptions options=PrettyDecoded) const
Returns the user info of the URL, or an empty string if the user info is undefined.
Definition qurl.cpp:2129
static QByteArray toAce(const QString &domain, AceProcessingOptions options={})
Definition qurl.cpp:3064
QUrl resolved(const QUrl &relative) const
Returns the result of the merge of this URL with relative.
Definition qurl.cpp:2725
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2800
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1882
QString host(ComponentFormattingOptions=FullyDecoded) const
Returns the host of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2340
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1991
int port(int defaultPort=-1) const
Definition qurl.cpp:2383
static QUrl fromEncoded(QByteArrayView input, ParsingMode mode=TolerantMode)
Parses input and returns the corresponding QUrl.
Definition qurl.cpp:2988
QSet< QString >::iterator it
QString socketPeerName(QIODevice *device)
QAbstractSocket::SocketState socketState(QIODevice *device)
Combined button and popup list for selecting options.
@ QueuedConnection
static void * context
#define Q_UNLIKELY(x)
static QString header(const QString &name)
EGLConfig config
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputPortEXT port
QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id)
static QByteArray makeAcceptLanguage()
static int getPreferredActiveChannelCount(QHttpNetworkConnection::ConnectionType type, int defaultValue)
QPair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair
static const QSystemLocale * systemLocale()
Definition qlocale.cpp:746
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLuint GLuint end
GLbitfield GLuint64 timeout
[4]
GLenum type
GLfloat GLfloat GLfloat GLfloat h
void ** params
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
QT_BEGIN_NAMESPACE constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
unsigned short quint16
Definition qtypes.h:48
long long qint64
Definition qtypes.h:60
QTcpSocket * socket
[1]
QQueue< int > queue
[0]
QNetworkRequest request(url)
QNetworkReply * reply
QHostInfo info
[0]
QNetworkProxy proxy
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...