Qt 6.x
The Qt SDK
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
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
32
33using namespace Qt::StringLiterals;
34
35// Note: Only used from auto tests, normal usage is via QHttp1Configuration
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
44
46 quint16 port, bool encrypt,
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.
66}
67
69 quint16 port, bool encrypt,
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{
81
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.
89}
90
91
92
94{
95 for (int i = 0; i < channelCount; ++i) {
96 if (channels[i].socket) {
97 QObject::disconnect(channels[i].socket, nullptr, &channels[i], nullptr);
99 delete channels[i].socket;
100 }
101 }
102 delete []channels;
103}
104
106{
108 for (int i = 0; i < channelCount; i++) {
109 channels[i].setConnection(this->q_func());
111 }
112
115}
116
118{
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)
127 else
128#endif
130 }
131 }
132}
133
135{
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)
143 else
144#endif
146
147 // Resume pending upload if needed
149 QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
150 }
151 }
152
153 // queue _q_startNextRequest
154 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
155}
156
158{
159 for (int i = 0; i < activeChannelCount; ++i)
160 if (channels[i].socket == socket)
161 return i;
162
163 qFatal("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.
172{
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.
182 channels[otherSocket].ensureConnection();
183 }
184
188 channels[0].close();
189 emitError = true;
190 } else {
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.
200 channels[i].close();
201 emitError = true;
202 }
203 } else {
206 // First connection worked so this is the second one to complete and it failed.
207 channels[i].close();
208 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
209 emitError = false;
210 }
212 qWarning("We got a connection error when networkLayerState is Unknown");
213 }
214 }
215 return emitError;
216}
217
218
220{
221 return reply.d_func()->responseData.byteAmount();
222}
223
225{
226 return reply.d_func()->responseData.sizeNextBlock();
227}
228
230{
231 QHttpNetworkRequest &request = messagePair.first;
232 QHttpNetworkReply *reply = messagePair.second;
233
234 // add missing fields for the request
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("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
259 value = request.headerField("proxy-connection");
260 if (value.isEmpty())
261 request.setHeaderField("Proxy-Connection", "Keep-Alive");
262 } else {
263#endif
264 value = request.headerField("connection");
265 if (value.isEmpty())
266 request.setHeaderField("Connection", "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("accept-encoding");
277 if (value.isEmpty()) {
278#ifndef QT_NO_COMPRESS
279 const QByteArrayList &acceptedEncoding = QDecompressHelper::acceptedEncoding();
280 request.setHeaderField("Accept-Encoding", acceptedEncoding.join(", "));
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("accept-language");
293 if (value.isEmpty()) {
295 QString acceptLanguage;
296 if (systemLocale == "C"_L1)
297 acceptLanguage = QString::fromLatin1("en,*");
298 else if (systemLocale.startsWith("en-"_L1))
299 acceptLanguage = systemLocale + ",*"_L1;
300 else
301 acceptLanguage = systemLocale + ",en,*"_L1;
302 request.setHeaderField("Accept-Language", std::move(acceptLanguage).toLatin1());
303 }
304
305 // set the User Agent
306 value = request.headerField("user-agent");
307 if (value.isEmpty())
308 request.setHeaderField("User-Agent", "Mozilla/5.0");
309 // set the host
310 value = request.headerField("host");
311 if (value.isEmpty()) {
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(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("Host", host);
331 }
332
333 reply->d_func()->requestIsPrepared = true;
334}
335
336
337
338
342{
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, 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);
362 if (socket)
364
365 // send the next request
366 QMetaObject::invokeMethod(q, "_q_startNextRequest", 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) {
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(auth->user()))
396 otherAuth->setUser(auth->user());
397 if (otherAuth->password().compare(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
405 bool isProxy, bool &resend)
406{
409
410 resend = false;
411 //create the response header to be used with QAuthenticatorPrivate.
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(header);
417 const bool isSupported = std::any_of(authenticationMethods.begin(), authenticationMethods.end(),
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
424 //proceed with the authentication.
425 if (auth->isNull())
426 auth->detach();
428 priv->parseHttpResponse(fields, isProxy, 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 ||
435 && priv->method == QAuthenticatorPrivate::Ntlm)) {
438
440 if (!isProxy) {
441 if (channels[i].authenticationCredentialsSent) {
442 auth->detach();
444 priv->hasFailed = true;
447 }
448 emit reply->authenticationRequired(reply->request(), auth);
449#ifndef QT_NO_NETWORKPROXY
450 } else {
451 if (channels[i].proxyCredentialsSent) {
452 auth->detach();
454 priv->hasFailed = true;
457 }
458 emit reply->proxyAuthenticationRequired(networkProxy, auth);
459#endif
460 }
462
463 if (priv->phase != QAuthenticatorPrivate::Done) {
464 // send any pending requests
465 copyCredentials(i, auth, isProxy);
466 }
467 } else if (priv->phase == QAuthenticatorPrivate::Start) {
468 // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
469 // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
470 // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
471 emit reply->cacheCredentials(reply->request(), auth);
472 }
473 // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
474 // then nothing was filled in by the user or the cache
475 // - If withCredentials has been set to false (e.g. by Qt WebKit for a cross-origin XMLHttpRequest) then
476 // we need to bail out if authentication is required.
477 if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
478 // Reset authenticator so the next request on that channel does not get messed up
479 auth = nullptr;
480 if (isProxy)
482 else
484
485 // authentication is cancelled, send the current contents to the user.
486 emit reply->headerChanged();
489 isProxy
492 reply->d_func()->errorString = errorDetail(errorCode, socket);
493 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
494 // ### at this point the reply could be deleted
495 return true;
496 }
497 //resend the request
498 resend = true;
499 return true;
500 }
501 return false;
502}
503
504// Used by the HTTP1 code-path
507{
509 if (result.errorCode != QNetworkReply::NoError) {
510 emitReplyError(socket, reply, result.errorCode);
511 return {};
512 }
513 return std::move(result.redirectUrl);
514}
515
518{
519 if (!reply->request().isFollowRedirects())
520 return {{}, QNetworkReply::NoError};
521
522 QUrl redirectUrl;
524 for (const QNetworkReply::RawHeaderPair &header : fields) {
525 if (header.first.compare("location", Qt::CaseInsensitive) == 0) {
526 redirectUrl = QUrl::fromEncoded(header.second);
527 break;
528 }
529 }
530
531 // If the location url is invalid/empty, we return ProtocolUnknownError
532 if (!redirectUrl.isValid())
534
535 // Check if we have exceeded max redirects allowed
536 if (reply->request().redirectCount() <= 0)
538
539 // Resolve the URL if it's relative
540 if (redirectUrl.isRelative())
541 redirectUrl = reply->request().url().resolved(redirectUrl);
542
543 // Check redirect url protocol
544 const QUrl priorUrl(reply->request().url());
545 if (redirectUrl.scheme() == "http"_L1 || redirectUrl.scheme() == "https"_L1) {
546 switch (reply->request().redirectPolicy()) {
548 // Here we could handle https->http redirects as InsecureProtocolError.
549 // However, if HSTS is enabled and redirectUrl.host() is a known STS
550 // host, then we'll replace its scheme and this won't downgrade protocol,
551 // after all. We cannot access QNAM's STS cache from here, so delegate
552 // this check to QNetworkReplyHttpImpl.
553 break;
555 if (priorUrl.host() != redirectUrl.host()
556 || priorUrl.scheme() != redirectUrl.scheme()
557 || priorUrl.port() != redirectUrl.port()) {
559 }
560 break;
562 break;
563 default:
564 Q_ASSERT(!"Unexpected redirect policy");
565 }
566 } else {
568 }
569 return {std::move(redirectUrl), QNetworkReply::NoError};
570}
571
573{
575
577
578 QAuthenticator *authenticator = &channel.authenticator;
580 // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
581 if (priv && priv->method != QAuthenticatorPrivate::None) {
582 const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
584 const bool authNeeded = channel.lastStatus == 401;
585 const bool ntlmNegoOk = ntlmNego && authNeeded
587 || !channel.authenticationCredentialsSent);
588 const bool otherOk =
589 !ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty());
590 if (ntlmNegoOk || otherOk) {
591 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
592 request.url().host());
593 request.setHeaderField("Authorization", response);
594 channel.authenticationCredentialsSent = true;
595 }
596 }
597
598#if QT_CONFIG(networkproxy)
599 authenticator = &channel.proxyAuthenticator;
600 priv = QAuthenticatorPrivate::getPrivate(*authenticator);
601 // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
602 if (priv && priv->method != QAuthenticatorPrivate::None) {
603 const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
605 const bool proxyAuthNeeded = channel.lastStatus == 407;
606 const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded
607 && (priv->phase != QAuthenticatorPrivate::Done || !channel.proxyCredentialsSent);
608 const bool otherOk = !ntlmNego;
609 if (ntlmNegoOk || otherOk) {
610 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
612 request.setHeaderField("Proxy-Authorization", response);
613 channel.proxyCredentialsSent = true;
614 }
615 }
616#endif // QT_CONFIG(networkproxy)
617}
618
620{
622
623 // The reply component of the pair is created initially.
626 reply->d_func()->connection = q;
627 reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
629
630 if (request.isPreConnect())
632
635 switch (request.priority()) {
638 break;
642 break;
643 }
644 }
645 else { // HTTP/2 ('h2' mode)
646 if (!pair.second->d_func()->requestIsPrepared)
647 prepareRequest(pair);
649 }
650
651 // For Happy Eyeballs the networkLayerState is set to Unknown
652 // until we have started the first connection attempt. So no
653 // request will be started until we know if IPv4 or IPv6
654 // should be used.
657 } else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
658 // this used to be called via invokeMethod and a QueuedConnection
659 // It is the only place _q_startNextRequest is called directly without going
660 // through the event loop using a QueuedConnection.
661 // This is dangerous because of recursion that might occur when emitting
662 // signals as DirectConnection from this code path. Therefore all signal
663 // emissions that can come out from this code path need to
664 // be QueuedConnection.
665 // We are currently trying to fine-tune this.
667 }
668 return reply;
669}
670
672{
673 for (auto &pair : highPriorityQueue) {
674 if (!pair.second->d_func()->requestIsPrepared)
675 prepareRequest(pair);
677 }
678
680
681 for (auto &pair : lowPriorityQueue) {
682 if (!pair.second->d_func()->requestIsPrepared)
683 prepareRequest(pair);
684 channels[0].h2RequestsToSend.insert(pair.first.priority(), pair);
685 }
686
688}
689
691{
693
694 QHttpNetworkRequest request = pair.first;
695 switch (request.priority()) {
698 break;
702 break;
703 }
704
705 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
706}
707
709{
710 int i = 0;
711 if (socket)
712 i = indexOf(socket);
713
714 if (!highPriorityQueue.isEmpty()) {
715 // remove from queue before sendRequest! else we might pipeline the same request again
717 if (!messagePair.second->d_func()->requestIsPrepared)
718 prepareRequest(messagePair);
719 updateChannel(i, messagePair);
720 return true;
721 }
722
723 if (!lowPriorityQueue.isEmpty()) {
724 // remove from queue before sendRequest! else we might pipeline the same request again
726 if (!messagePair.second->d_func()->requestIsPrepared)
727 prepareRequest(messagePair);
728 updateChannel(i, messagePair);
729 return true;
730 }
731 return false;
732}
733
735{
736 channels[i].request = messagePair.first;
737 channels[i].reply = messagePair.second;
738 // Now that reply is assigned a channel, correct reply to channel association
739 // previously set in queueRequest.
740 channels[i].reply->d_func()->connectionChannel = &channels[i];
741}
742
744{
746 return highPriorityQueue.last().first;
748 return lowPriorityQueue.last().first;
749 return QHttpNetworkRequest();
750}
751
753{
755 return highPriorityQueue.last().second;
757 return lowPriorityQueue.last().second;
758 return nullptr;
759}
760
761// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
763{
764 // return fast if there is nothing to pipeline
766 return;
767
768 int i = indexOf(socket);
769
770 // return fast if there was no reply right now processed
771 if (channels[i].reply == nullptr)
772 return;
773
774 if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.size() >= defaultRePipelineLength)) {
775 return;
776 }
777
779 return;
780
781 // the current request that is in must already support pipelining
782 if (!channels[i].request.isPipeliningAllowed())
783 return;
784
785 // the current request must be a idempotent (right now we only check GET)
786 if (channels[i].request.operation() != QHttpNetworkRequest::Get)
787 return;
788
789 // check if socket is connected
791 return;
792
793 // check for resendCurrent
794 if (channels[i].resendCurrent)
795 return;
796
797 // we do not like authentication stuff
798 // ### make sure to be OK with this in later releases
799 if (!channels[i].authenticator.isNull()
802 return;
803 if (!channels[i].proxyAuthenticator.isNull()
806 return;
807
808 // must be in ReadingState or WaitingState
811 return;
812
813 int lengthBefore;
814 while (!highPriorityQueue.isEmpty()) {
815 lengthBefore = channels[i].alreadyPipelinedRequests.size();
817
818 if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
820 return;
821 }
822
823 if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
824 break; // did not process anything, now do the low prio queue
825 }
826
827 while (!lowPriorityQueue.isEmpty()) {
828 lengthBefore = channels[i].alreadyPipelinedRequests.size();
830
831 if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
833 return;
834 }
835
836 if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
837 break; // did not process anything
838 }
839
840
842}
843
844// returns true when the processing of a queue has been done
846{
847 if (queue.isEmpty())
848 return true;
849
850 for (int i = queue.size() - 1; i >= 0; --i) {
851 HttpMessagePair messagePair = queue.at(i);
852 const QHttpNetworkRequest &request = messagePair.first;
853
854 // we currently do not support pipelining if HTTP authentication is used
855 if (!request.url().userInfo().isEmpty())
856 continue;
857
858 // take only GET requests
859 if (request.operation() != QHttpNetworkRequest::Get)
860 continue;
861
862 if (!request.isPipeliningAllowed())
863 continue;
864
865 // remove it from the queue
866 queue.takeAt(i);
867 // we modify the queue we iterate over here, but since we return from the function
868 // afterwards this is fine.
869
870 // actually send it
871 if (!messagePair.second->d_func()->requestIsPrepared)
872 prepareRequest(messagePair);
873 channel.pipelineInto(messagePair);
874
875 // return false because we processed something and need to process again
876 return false;
877 }
878
879 // return true, the queue has been processed and not changed
880 return true;
881}
882
883
885{
886 QString errorString;
887 switch (errorCode) {
889 if (socket)
890 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
891 else
892 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(hostName);
893 break;
895 errorString = QCoreApplication::translate("QHttp", "Connection refused");
896 break;
898 errorString = QCoreApplication::translate("QHttp", "Connection closed");
899 break;
901 errorString = QCoreApplication::translate("QAbstractSocket", "Socket operation timed out");
902 break;
904 errorString = QCoreApplication::translate("QHttp", "Proxy requires authentication");
905 break;
907 errorString = QCoreApplication::translate("QHttp", "Host requires authentication");
908 break;
910 errorString = QCoreApplication::translate("QHttp", "Data corrupted");
911 break;
913 errorString = QCoreApplication::translate("QHttp", "Unknown protocol specified");
914 break;
916 errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
917 if (socket)
918 errorString += QStringLiteral(": ") + socket->errorString();
919 break;
921 errorString = QCoreApplication::translate("QHttp", "Too many redirects");
922 break;
924 errorString = QCoreApplication::translate("QHttp", "Insecure redirect");
925 break;
926 default:
927 // all other errors are treated as QNetworkReply::UnknownNetworkError
928 errorString = extraDetail;
929 break;
930 }
931 return errorString;
932}
933
934// this is called from the destructor of QHttpNetworkReply. It is called when
935// the reply was finished correctly or when it was aborted.
937{
939
940 // check if the reply is currently being processed or it is pipelined in
941 for (int i = 0; i < activeChannelCount; ++i) {
942 // is the reply associated the currently processing of this channel?
943 if (channels[i].reply == reply) {
944 channels[i].reply = nullptr;
945 if (channels[i].protocolHandler)
946 channels[i].protocolHandler->setReply(nullptr);
948 channels[i].resendCurrent = false;
949
951 // the reply had to be prematurely removed, e.g. it was not finished
952 // therefore we have to requeue the already pipelined requests.
954 }
955
956 // if HTTP mandates we should close
957 // or the reply is not finished yet, e.g. it was aborted
958 // we have to close that connection
959 if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished()) {
960 if (reply->isAborted()) {
961 channels[i].abort();
962 } else {
963 channels[i].close();
964 }
965 }
966
967 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
968 return;
969 }
970
971 // is the reply inside the pipeline of this channel already?
972 for (int j = 0; j < channels[i].alreadyPipelinedRequests.size(); j++) {
973 if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
974 // Remove that HttpMessagePair
976
978
979 // Since some requests had already been pipelined, but we removed
980 // one and re-queued the others
981 // we must force a connection close after the request that is
982 // currently in processing has been finished.
983 if (channels[i].reply)
984 channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
985
986 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
987 return;
988 }
989 }
990#ifndef QT_NO_SSL
991 // is the reply inside the H2 pipeline of this channel already?
994 for (; it != end; ++it) {
995 if (it.value().second == reply) {
997
998 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
999 return;
1000 }
1001 }
1002#endif
1003 }
1004 // remove from the high priority queue
1005 if (!highPriorityQueue.isEmpty()) {
1006 for (int j = highPriorityQueue.size() - 1; j >= 0; --j) {
1007 HttpMessagePair messagePair = highPriorityQueue.at(j);
1008 if (messagePair.second == reply) {
1010 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1011 return;
1012 }
1013 }
1014 }
1015 // remove from the low priority queue
1016 if (!lowPriorityQueue.isEmpty()) {
1017 for (int j = lowPriorityQueue.size() - 1; j >= 0; --j) {
1018 HttpMessagePair messagePair = lowPriorityQueue.at(j);
1019 if (messagePair.second == reply) {
1021 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1022 return;
1023 }
1024 }
1025 }
1026}
1027
1028
1029
1030// This function must be called from the event loop. The only
1031// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
1032// although it is called _q_startNextRequest, it will actually start multiple requests when possible
1034{
1035 // If there is no network layer state decided we should not start any new requests.
1037 return;
1038
1039 // If the QHttpNetworkConnection is currently paused then bail out immediately
1040 if (state == PausedState)
1041 return;
1042
1043 //resend the necessary ones.
1044 for (int i = 0; i < activeChannelCount; ++i) {
1046 channels[i].resendCurrent = false;
1047
1048 // if this is not possible, error will be emitted and connection terminated
1049 if (!channels[i].resetUploadData())
1050 continue;
1052 }
1053 }
1054
1055 // dequeue new ones
1056
1057 switch (connectionType) {
1059 // return fast if there is nothing to do
1061 return;
1062
1063 // try to get a free AND connected socket
1064 for (int i = 0; i < activeChannelCount; ++i) {
1065 if (channels[i].socket) {
1066 if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
1069 }
1070 }
1071 }
1072 break;
1073 }
1076 if (channels[0].h2RequestsToSend.isEmpty() && !channels[0].reply
1078 return;
1079 }
1080
1081 if (networkLayerState == IPv4)
1083 else if (networkLayerState == IPv6)
1087 && !channels[0].pendingEncrypt) {
1088 if (channels[0].h2RequestsToSend.size()) {
1089 channels[0].sendRequest();
1090 } else if (!channels[0].reply && !channels[0].switchedToHttp2) {
1091 // This covers an edge-case where we're already connected and the "connected"
1092 // signal was already sent, but we didn't have any request available at the time,
1093 // so it was missed. As such we need to dequeue a request and send it now that we
1094 // have one.
1096 channels[0].sendRequest();
1097 }
1098 }
1099 break;
1100 }
1101 }
1102
1103 // try to push more into all sockets
1104 // ### FIXME we should move this to the beginning of the function
1105 // as soon as QtWebkit is properly using the pipelining
1106 // (e.g. not for XMLHttpRequest or the first page load)
1107 // ### FIXME we should also divide the requests more even
1108 // on the connected sockets
1109 //tryToFillPipeline(socket);
1110 // return fast if there is nothing to pipeline
1112 return;
1113 for (int i = 0; i < activeChannelCount; i++)
1116
1117 // If there is not already any connected channels we need to connect a new one.
1118 // We do not pair the channel with the request until we know if it is
1119 // connected or not. This is to reuse connected channels before we connect new once.
1120 int queuedRequests = highPriorityQueue.size() + lowPriorityQueue.size();
1121
1122 // in case we have in-flight preconnect requests and normal requests,
1123 // we only need one socket for each (preconnect, normal request) pair
1124 int neededOpenChannels = queuedRequests;
1125 if (preConnectRequests > 0) {
1126 int normalRequests = queuedRequests - preConnectRequests;
1127 neededOpenChannels = qMax(normalRequests, preConnectRequests);
1128 }
1129
1130 if (neededOpenChannels <= 0)
1131 return;
1132
1133 QQueue<int> channelsToConnect;
1134
1135 // use previously used channels first
1136 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1137 if (!channels[i].socket)
1138 continue;
1139
1142 || channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState"
1143 neededOpenChannels--;
1144 continue;
1145 }
1146
1147 if (!channels[i].reply && !channels[i].isSocketBusy()
1149 channelsToConnect.enqueue(i);
1150 neededOpenChannels--;
1151 }
1152 }
1153
1154 // use other channels
1155 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1156 if (channels[i].socket)
1157 continue;
1158
1159 channelsToConnect.enqueue(i);
1160 neededOpenChannels--;
1161 }
1162
1163 while (!channelsToConnect.isEmpty()) {
1164 const int channel = channelsToConnect.dequeue();
1165
1166 if (networkLayerState == IPv4)
1168 else if (networkLayerState == IPv6)
1170
1172 }
1173}
1174
1175
1177{
1178 for (int i = 0 ; i < activeChannelCount; ++i) {
1179 if (channels[i].reply == reply) {
1180 // emulate a readyRead() from the socket
1182 return;
1183 }
1184 }
1185}
1186
1187
1188
1189// The first time we start the connection is used we do not know if we
1190// should use IPv4 or IPv6. So we start a hostlookup to figure this out.
1191// Later when we do the connection the socket will not need to do another
1192// lookup as then the hostinfo will already be in the cache.
1194{
1196
1197 // check if we already now can decide if this is IPv4 or IPv6
1198 QString lookupHost = hostName;
1199#ifndef QT_NO_NETWORKPROXY
1201 lookupHost = networkProxy.hostName();
1203 lookupHost = channels[0].proxy.hostName();
1204 }
1205#endif
1206 QHostAddress temp;
1207 if (temp.setAddress(lookupHost)) {
1208 const QAbstractSocket::NetworkLayerProtocol protocol = temp.protocol();
1209 if (protocol == QAbstractSocket::IPv4Protocol) {
1211 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1212 return;
1213 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1215 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1216 return;
1217 }
1218 } else {
1219 int hostLookupId;
1220 bool immediateResultValid = false;
1221 QHostInfo hostInfo = qt_qhostinfo_lookup(lookupHost,
1222 this->q_func(),
1224 &immediateResultValid,
1225 &hostLookupId);
1226 if (immediateResultValid) {
1227 _q_hostLookupFinished(hostInfo);
1228 }
1229 }
1230}
1231
1232
1234{
1235 bool bIpv4 = false;
1236 bool bIpv6 = false;
1237 bool foundAddress = false;
1239 return;
1240
1241 const auto addresses = info.addresses();
1242 for (const QHostAddress &address : addresses) {
1243 const QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
1244 if (protocol == QAbstractSocket::IPv4Protocol) {
1245 if (!foundAddress) {
1246 foundAddress = true;
1247 delayIpv4 = false;
1248 }
1249 bIpv4 = true;
1250 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1251 if (!foundAddress) {
1252 foundAddress = true;
1253 delayIpv4 = true;
1254 }
1255 bIpv6 = true;
1256 }
1257 }
1258
1259 if (bIpv4 && bIpv6)
1261 else if (bIpv4) {
1263 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1264 } else if (bIpv6) {
1266 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1267 } else {
1268 auto lookupError = QNetworkReply::HostNotFoundError;
1269#ifndef QT_NO_NETWORKPROXY
1270 // if the proxy can lookup hostnames, all hostname lookups except for the lookup of the
1271 // proxy hostname are delegated to the proxy.
1272 auto proxyCapabilities = networkProxy.capabilities() | channels[0].proxy.capabilities();
1273 if (proxyCapabilities & QNetworkProxy::HostNameLookupCapability)
1275#endif
1276 if (dequeueRequest(channels[0].socket)) {
1277 emitReplyError(channels[0].socket, channels[0].reply, lookupError);
1281 for (const HttpMessagePair &h2Pair : std::as_const(channels[0].h2RequestsToSend)) {
1282 // emit error for all replies
1283 QHttpNetworkReply *currentReply = h2Pair.second;
1284 Q_ASSERT(currentReply);
1285 emitReplyError(channels[0].socket, currentReply, lookupError);
1286 }
1287 } else {
1288 // We can end up here if a request has been aborted or otherwise failed (e.g. timeout)
1289 // before the host lookup was finished.
1290 qDebug("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
1291 " could not de-queue request, failed to report HostNotFoundError");
1293 }
1294 }
1295}
1296
1297
1298// This will be used if the host lookup found both and Ipv4 and
1299// Ipv6 address. Then we will start up two connections and pick
1300// the network layer of the one that finish first. The second
1301// connection will then be disconnected.
1303{
1304 if (activeChannelCount > 1) {
1305 // At this time all channels should be unconnected.
1306 Q_ASSERT(!channels[0].isSocketBusy());
1307 Q_ASSERT(!channels[1].isSocketBusy());
1308
1310
1313
1314 int timeout = 300;
1316 if (delayIpv4)
1318 else
1320 } else {
1324 }
1325}
1326
1328{
1329 for (int i = 0 ; i < activeChannelCount; ++i) {
1330 if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
1331 channels[i].close();
1332 }
1333 }
1334}
1335
1337{
1338 if (delayIpv4)
1340 else
1342}
1343
1346 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt , connectionType)), parent)
1347{
1349 d->init();
1353 }
1354}
1355
1357 quint16 port, bool encrypt, QObject *parent,
1359 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
1360 connectionType)), parent)
1361{
1363 d->init();
1367 }
1368}
1369
1371{
1372}
1373
1375{
1376 Q_D(const QHttpNetworkConnection);
1377 return d->hostName;
1378}
1379
1381{
1382 Q_D(const QHttpNetworkConnection);
1383 return d->port;
1384}
1385
1387{
1389 return d->queueRequest(request);
1390}
1391
1393{
1395 d->fillHttp2Queue();
1396}
1397
1399{
1400 Q_D(const QHttpNetworkConnection);
1401 return d->encrypt;
1402}
1403
1405{
1406 return d_func()->channels;
1407}
1408
1409#ifndef QT_NO_NETWORKPROXY
1411{
1413 d->networkProxy = networkProxy;
1414 // update the authenticator
1415 if (!d->networkProxy.user().isEmpty()) {
1416 for (int i = 0; i < d->channelCount; ++i) {
1417 d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
1418 d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
1419 }
1420 }
1421}
1422
1424{
1425 Q_D(const QHttpNetworkConnection);
1426 return d->networkProxy;
1427}
1428
1430{
1432 for (int i = 0; i < d->channelCount; ++i)
1433 d->channels[i].setProxy(networkProxy);
1434}
1435
1437{
1438 Q_D(const QHttpNetworkConnection);
1439 return d->channels[0].proxy;
1440}
1441#endif
1442
1444{
1446 return d->connectionType;
1447}
1448
1450{
1452 d->connectionType = type;
1453}
1454
1456{
1457 Q_D(const QHttpNetworkConnection);
1458 return d->http2Parameters;
1459}
1460
1462{
1464 d->http2Parameters = params;
1465}
1466
1467// SSL support below
1468#ifndef QT_NO_SSL
1470{
1472 if (!d->encrypt)
1473 return;
1474
1475 // set the config on all channels
1476 for (int i = 0; i < d->activeChannelCount; ++i)
1477 d->channels[i].setSslConfiguration(config);
1478}
1479
1480std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext()
1481{
1483 return d->sslContext;
1484}
1485
1486void QHttpNetworkConnection::setSslContext(std::shared_ptr<QSslContext> context)
1487{
1489 d->sslContext = std::move(context);
1490}
1491
1493{
1495 if (!d->encrypt)
1496 return;
1497
1498 if (channel == -1) { // ignore for all channels
1499 // We need to ignore for all channels, even the ones that are not in use just in case they
1500 // will be in the future.
1501 for (int i = 0; i < d->channelCount; ++i) {
1502 d->channels[i].ignoreSslErrors();
1503 }
1504
1505 } else {
1506 d->channels[channel].ignoreSslErrors();
1507 }
1508}
1509
1511{
1513 if (!d->encrypt)
1514 return;
1515
1516 if (channel == -1) { // ignore for all channels
1517 // We need to ignore for all channels, even the ones that are not in use just in case they
1518 // will be in the future.
1519 for (int i = 0; i < d->channelCount; ++i) {
1520 d->channels[i].ignoreSslErrors(errors);
1521 }
1522
1523 } else {
1524 d->channels[channel].ignoreSslErrors(errors);
1525 }
1526}
1527
1528#endif //QT_NO_SSL
1529
1531{
1532 d_func()->preConnectRequests--;
1533}
1534
1536{
1537 Q_D(const QHttpNetworkConnection);
1538 return d->peerVerifyName;
1539}
1540
1542{
1544 d->peerVerifyName = peerName;
1545}
1546
1548{
1550
1551 if (isOnline) {
1552 // If we did not have any 'isOffline' previously - well, good
1553 // to know, we are 'online' apparently.
1554 return;
1555 }
1556
1557 for (int i = 0; i < d->activeChannelCount; i++) {
1558 auto &channel = d->channels[i];
1559 channel.emitFinishedWithError(QNetworkReply::TemporaryNetworkFailureError, "Temporary network failure.");
1560 channel.close();
1561 }
1562
1563 // We don't care, this connection is broken from our POV.
1564 d->connectionMonitor.stopMonitoring();
1565}
1566
1567#ifndef QT_NO_NETWORKPROXY
1568// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
1569// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
1570// e.g. it is for SOCKS proxies which require authentication.
1572{
1573 // Also pause the connection because socket notifiers may fire while an user
1574 // dialog is displaying
1578 && (chan->switchedToHttp2 || chan->h2RequestsToSend.size() > 0))
1580 // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
1581 // but that does not matter because the signal will ultimately be emitted
1582 // by the QNetworkAccessManager.
1583 Q_ASSERT(chan->h2RequestsToSend.size() > 0);
1584 reply = chan->h2RequestsToSend.cbegin().value().second;
1585 } else { // HTTP
1586 reply = chan->reply;
1587 }
1588
1589 Q_ASSERT(reply);
1590 emit reply->proxyAuthenticationRequired(proxy, auth);
1592 int i = indexOf(chan->socket);
1593 copyCredentials(i, auth, true);
1594}
1595#endif
1596
1597
1599
1600#include "moc_qhttpnetworkconnection_p.cpp"
IOBluetoothL2CAPChannel * channel
static void pauseSocketNotifiers(QAbstractSocket *)
static void resumeSocketNotifiers(QAbstractSocket *)
The QAbstractSocket class provides the base functionality common to all socket types.
static constexpr auto IPv4Protocol
QString peerName() const
Returns the name of the peer as specified by connectToHost(), or an empty QString if connectToHost() ...
static constexpr auto AnyIPProtocol
void close() override
Closes the I/O device for the socket and calls disconnectFromHost() to close the socket's connection.
SocketState state() const
Returns the state of the socket.
static constexpr auto IPv6Protocol
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.
void setPassword(const QString &password)
Sets the password used for authentication.
QString password() const
Returns the password used for authentication.
bool isNull() const
Returns true if the object has not been initialized.
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 constexpr QChar fromLatin1(char c) noexcept
Converts the Latin-1 character c to its equivalent QChar.
Definition qchar.h:461
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
The QHttp2Configuration class controls HTTP/2 parameters and settings.
std::unique_ptr< QAbstractProtocolHandler > protocolHandler
QAbstractSocket::NetworkLayerProtocol networkLayerPreference
QMultiMap< int, HttpMessagePair > h2RequestsToSend
void setConnection(QHttpNetworkConnection *c)
QHttpNetworkConnection::ConnectionType connectionType
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
int indexOf(QAbstractSocket *socket) const
QHttpNetworkRequest predictNextRequest() const
void prepareRequest(HttpMessagePair &request)
void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator *auth)
NetworkLayerPreferenceState networkLayerState
QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt, QHttpNetworkConnection::ConnectionType type)
void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail=QString())
void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
QHttpNetworkConnectionChannel * channels
bool shouldEmitChannelError(QAbstractSocket *socket)
void networkLayerDetected(QAbstractSocket::NetworkLayerProtocol protocol)
void requeueRequest(const HttpMessagePair &pair)
QList< HttpMessagePair > lowPriorityQueue
void removeReply(QHttpNetworkReply *reply)
void _q_hostLookupFinished(const QHostInfo &info)
bool dequeueRequest(QAbstractSocket *socket)
QHttpNetworkReply * predictNextRequestsReply() const
QList< HttpMessagePair > highPriorityQueue
void updateChannel(int i, const HttpMessagePair &messagePair)
void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode)
bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend)
QHttpNetworkReply * queueRequest(const QHttpNetworkRequest &request)
void readMoreLater(QHttpNetworkReply *reply)
void fillPipeline(QAbstractSocket *socket)
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
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)
QHttpNetworkConnection(const QString &hostName, quint16 port=80, bool encrypt=false, ConnectionType connectionType=ConnectionTypeHTTP, QObject *parent=nullptr)
QNetworkProxy transparentProxy() const
std::shared_ptr< QSslContext > sslContext()
void setSslContext(std::shared_ptr< QSslContext > context)
void setTransparentProxy(const QNetworkProxy &networkProxy)
void setHttp2Parameters(const QHttp2Configuration &params)
void ignoreSslErrors(int channel=-1)
void setSslConfiguration(const QSslConfiguration &config)
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.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
T & last()
Definition qlist.h:631
void removeAt(qsizetype i)
Definition qlist.h:573
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
value_type takeLast()
Definition qlist.h:550
void prepend(rvalue_ref t)
Definition qlist.h:456
void clear()
Definition qlist.h:417
QString name() const
The short name of this locale.
Definition qlocale.cpp:1340
static QLocale system()
Returns a QLocale object initialized to the system locale.
Definition qlocale.cpp:2742
iterator end()
Definition qmap.h:1300
iterator insert(const Key &key, const T &value)
Definition qmap.h:1425
size_type size() const
Definition qmap.h:911
size_type remove(const Key &key)
Definition qmap.h:944
const_iterator cbegin() const
Definition qmap.h:1299
iterator begin()
Definition qmap.h:1296
void reachabilityChanged(bool isOnline)
The QNetworkProxy class provides a network layer proxy.
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
QPair< QByteArray, QByteArray > RawHeaderPair
RawHeaderPair is a QPair<QByteArray, QByteArray> where the first QByteArray is the header name and th...
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:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
\inmodule QtCore
Definition qqueue.h:14
void enqueue(const T &t)
Adds value t to the tail of the queue.
Definition qqueue.h:18
T dequeue()
Removes the head item in the queue and returns it.
Definition qqueue.h:19
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:127
QByteArray toLatin1() const &
Definition qstring.h:559
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QString first(qsizetype n) const
Definition qstring.h:337
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
void setSingleShot(bool singleShot)
Definition qtimer.cpp:580
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:208
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
Definition qtimer.cpp:156
void stop()
Stops the timer.
Definition qtimer.cpp:226
\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:2126
static QByteArray toAce(const QString &domain, AceProcessingOptions options={})
Definition qurl.cpp:3061
QUrl resolved(const QUrl &relative) const
Returns the result of the merge of this URL with relative.
Definition qurl.cpp:2722
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2797
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1874
QString host(ComponentFormattingOptions=FullyDecoded) const
Returns the host of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2337
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1983
int port(int defaultPort=-1) const
Definition qurl.cpp:2380
static QUrl fromEncoded(QByteArrayView input, ParsingMode mode=TolerantMode)
Parses input and returns the corresponding QUrl.
Definition qurl.cpp:2985
QSet< QString >::iterator it
else opt state
[0]
Combined button and popup list for selecting options.
@ CaseInsensitive
@ 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)
QPair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair
static const QSystemLocale * systemLocale()
Definition qlocale.cpp:743
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
#define qFatal
Definition qlogging.h:164
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:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLuint GLuint end
GLbitfield GLuint64 timeout
[4]
GLenum type
void ** params
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
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 QStringLiteral(str)
#define emit
unsigned short quint16
Definition qtypes.h:43
long long qint64
Definition qtypes.h:55
QFileInfo info(fileName)
[8]
QTcpSocket * socket
[1]
QQueue< int > queue
[0]
QNetworkRequest request(url)
QNetworkReply * reply
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...
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent