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 (isLocalSocket && value.isEmpty()) {
308 // The local socket connections might have a full file path, and that
309 // may not be suitable for the Host header. But we can use whatever the
310 // user has set in the URL.
311 request.prependHeaderField("Host", request.url().host().toLocal8Bit());
312 } else if (value.isEmpty()) {
314 QByteArray host;
315 if (add.setAddress(hostName)) {
316 if (add.protocol() == QAbstractSocket::IPv6Protocol)
317 host = (u'[' + hostName + u']').toLatin1(); //format the ipv6 in the standard way
318 else
319 host = hostName.toLatin1();
320
321 } else {
322 host = QUrl::toAce(hostName);
323 }
324
325 int port = request.url().port();
326 if (port != -1) {
327 host += ':';
328 host += QByteArray::number(port);
329 }
330
331 request.prependHeaderField("Host", host);
332 }
333
334 reply->d_func()->requestIsPrepared = true;
335}
336
337
338
339
343{
345
346 int i = 0;
347 if (socket)
348 i = indexOf(socket);
349
350 if (reply) {
351 // this error matters only to this reply
352 reply->d_func()->errorString = errorDetail(errorCode, socket);
353 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
354 // remove the corrupt data if any
355 reply->d_func()->eraseData();
356
357 // Clean the channel
358 channels[i].close();
359 channels[i].reply = nullptr;
360 if (channels[i].protocolHandler)
361 channels[i].protocolHandler->setReply(nullptr);
363 if (socket)
365
366 // send the next request
367 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
368 }
369}
370
371void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
372{
373 Q_ASSERT(auth);
374
375 // NTLM and Negotiate do multi-phase authentication.
376 // Copying credentialsbetween authenticators would mess things up.
377 if (fromChannel >= 0) {
379 if (priv
380 && (priv->method == QAuthenticatorPrivate::Ntlm
381 || priv->method == QAuthenticatorPrivate::Negotiate)) {
382 return;
383 }
384 }
385
386 // select another channel
387 QAuthenticator* otherAuth = nullptr;
388 for (int i = 0; i < activeChannelCount; ++i) {
389 if (i == fromChannel)
390 continue;
391 if (isProxy)
392 otherAuth = &channels[i].proxyAuthenticator;
393 else
394 otherAuth = &channels[i].authenticator;
395 // if the credentials are different, copy them
396 if (otherAuth->user().compare(auth->user()))
397 otherAuth->setUser(auth->user());
398 if (otherAuth->password().compare(auth->password()))
399 otherAuth->setPassword(auth->password());
400 }
401}
402
403
404// handles the authentication for one channel and eventually re-starts the other channels
406 bool isProxy, bool &resend)
407{
410
411 resend = false;
412 //create the response header to be used with QAuthenticatorPrivate.
413 const auto headers = reply->header();
414
415 // Check that any of the proposed authenticate methods are supported
416 const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
417 const QByteArrayList &authenticationMethods = reply->d_func()->headerFieldValues(header);
418 const bool isSupported = std::any_of(authenticationMethods.begin(), authenticationMethods.end(),
420 if (isSupported) {
421 int i = indexOf(socket);
422 //Use a single authenticator for all domains. ### change later to use domain/realm
423 QAuthenticator *auth = isProxy ? &channels[i].proxyAuthenticator
425 //proceed with the authentication.
426 if (auth->isNull())
427 auth->detach();
429 priv->parseHttpResponse(headers, isProxy, reply->url().host());
430 // Update method in case it changed
431 if (priv->method == QAuthenticatorPrivate::None)
432 return false;
433
434 if (priv->phase == QAuthenticatorPrivate::Done ||
436 && (priv->method == QAuthenticatorPrivate::Ntlm
437 || priv->method == QAuthenticatorPrivate::Negotiate))) {
440
442 if (!isProxy) {
443 if (channels[i].authenticationCredentialsSent) {
444 auth->detach();
446 priv->hasFailed = true;
449 }
450 emit reply->authenticationRequired(reply->request(), auth);
451#ifndef QT_NO_NETWORKPROXY
452 } else {
453 if (channels[i].proxyCredentialsSent) {
454 auth->detach();
456 priv->hasFailed = true;
459 }
460 emit reply->proxyAuthenticationRequired(networkProxy, auth);
461#endif
462 }
464
465 if (priv->phase != QAuthenticatorPrivate::Done) {
466 // send any pending requests
467 copyCredentials(i, auth, isProxy);
468 }
469 } else if (priv->phase == QAuthenticatorPrivate::Start) {
470 // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
471 // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
472 // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
473 emit reply->cacheCredentials(reply->request(), auth);
474 }
475 // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
476 // then nothing was filled in by the user or the cache
477 // - If withCredentials has been set to false (e.g. by Qt WebKit for a cross-origin XMLHttpRequest) then
478 // we need to bail out if authentication is required.
479 if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
480 // Reset authenticator so the next request on that channel does not get messed up
481 auth = nullptr;
482 if (isProxy)
484 else
486
487 // authentication is cancelled, send the current contents to the user.
488 emit reply->headerChanged();
491 isProxy
494 reply->d_func()->errorString = errorDetail(errorCode, socket);
495 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
496 // ### at this point the reply could be deleted
497 return true;
498 }
499 //resend the request
500 resend = true;
501 return true;
502 }
503 return false;
504}
505
506// Used by the HTTP1 code-path
517
520{
521 if (!reply->request().isFollowRedirects())
522 return {{}, QNetworkReply::NoError};
523
524 QUrl redirectUrl;
525 const QHttpHeaders fields = reply->header();
526 if (const auto h = fields.values(QHttpHeaders::WellKnownHeader::Location); !h.empty()) {
527 redirectUrl = QUrl::fromEncoded(h.first());
528 }
529
530 // If the location url is invalid/empty, we return ProtocolUnknownError
531 if (!redirectUrl.isValid())
533
534 // Check if we have exceeded max redirects allowed
535 if (reply->request().redirectCount() <= 0)
537
538 // Resolve the URL if it's relative
539 if (redirectUrl.isRelative())
540 redirectUrl = reply->request().url().resolved(redirectUrl);
541
542 // Check redirect url protocol
543 const QUrl priorUrl(reply->request().url());
544 const QString targetUrlScheme = redirectUrl.scheme();
545 if (targetUrlScheme == "http"_L1 || targetUrlScheme == "https"_L1
546 || targetUrlScheme.startsWith("unix"_L1)) {
547 switch (reply->request().redirectPolicy()) {
549 // Here we could handle https->http redirects as InsecureProtocolError.
550 // However, if HSTS is enabled and redirectUrl.host() is a known STS
551 // host, then we'll replace its scheme and this won't downgrade protocol,
552 // after all. We cannot access QNAM's STS cache from here, so delegate
553 // this check to QNetworkReplyHttpImpl.
554 break;
556 if (priorUrl.host() != redirectUrl.host()
557 || priorUrl.scheme() != targetUrlScheme
558 || priorUrl.port() != redirectUrl.port()) {
560 }
561 break;
563 break;
564 default:
565 Q_ASSERT(!"Unexpected redirect policy");
566 }
567 } else {
569 }
570 return {std::move(redirectUrl), QNetworkReply::NoError};
571}
572
574{
576
578
579 QAuthenticator *authenticator = &channel.authenticator;
581 // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
582 if (priv && priv->method != QAuthenticatorPrivate::None) {
583 const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
585 const bool authNeeded = channel.lastStatus == 401;
586 const bool ntlmNegoOk = ntlmNego && authNeeded
588 || !channel.authenticationCredentialsSent);
589 const bool otherOk =
590 !ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty());
591 if (ntlmNegoOk || otherOk) {
592 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
593 request.url().host());
594 request.setHeaderField("Authorization", response);
595 channel.authenticationCredentialsSent = true;
596 }
597 }
598
599#if QT_CONFIG(networkproxy)
600 authenticator = &channel.proxyAuthenticator;
601 priv = QAuthenticatorPrivate::getPrivate(*authenticator);
602 // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
603 if (priv && priv->method != QAuthenticatorPrivate::None) {
604 const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
606 const bool proxyAuthNeeded = channel.lastStatus == 407;
607 const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded
608 && (priv->phase != QAuthenticatorPrivate::Done || !channel.proxyCredentialsSent);
609 const bool otherOk = !ntlmNego;
610 if (ntlmNegoOk || otherOk) {
611 QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
613 request.setHeaderField("Proxy-Authorization", response);
614 channel.proxyCredentialsSent = true;
615 }
616 }
617#endif // QT_CONFIG(networkproxy)
618}
619
621{
623
624 // The reply component of the pair is created initially.
627 reply->d_func()->connection = q;
628 reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
630
631 if (request.isPreConnect())
633
636 switch (request.priority()) {
639 break;
643 break;
644 }
645 }
646 else { // HTTP/2 ('h2' mode)
647 if (!pair.second->d_func()->requestIsPrepared)
648 prepareRequest(pair);
650 }
651
652 // For Happy Eyeballs the networkLayerState is set to Unknown
653 // until we have started the first connection attempt. So no
654 // request will be started until we know if IPv4 or IPv6
655 // should be used.
658 } else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
659 // this used to be called via invokeMethod and a QueuedConnection
660 // It is the only place _q_startNextRequest is called directly without going
661 // through the event loop using a QueuedConnection.
662 // This is dangerous because of recursion that might occur when emitting
663 // signals as DirectConnection from this code path. Therefore all signal
664 // emissions that can come out from this code path need to
665 // be QueuedConnection.
666 // We are currently trying to fine-tune this.
668 }
669 return reply;
670}
671
673{
674 for (auto &pair : highPriorityQueue) {
675 if (!pair.second->d_func()->requestIsPrepared)
676 prepareRequest(pair);
678 }
679
681
682 for (auto &pair : lowPriorityQueue) {
683 if (!pair.second->d_func()->requestIsPrepared)
684 prepareRequest(pair);
685 channels[0].h2RequestsToSend.insert(pair.first.priority(), pair);
686 }
687
689}
690
708
710{
711 int i = 0;
712 if (socket)
713 i = indexOf(socket);
714
715 if (!highPriorityQueue.isEmpty()) {
716 // remove from queue before sendRequest! else we might pipeline the same request again
718 if (!messagePair.second->d_func()->requestIsPrepared)
719 prepareRequest(messagePair);
720 updateChannel(i, messagePair);
721 return true;
722 }
723
724 if (!lowPriorityQueue.isEmpty()) {
725 // remove from queue before sendRequest! else we might pipeline the same request again
727 if (!messagePair.second->d_func()->requestIsPrepared)
728 prepareRequest(messagePair);
729 updateChannel(i, messagePair);
730 return true;
731 }
732 return false;
733}
734
736{
737 channels[i].request = messagePair.first;
738 channels[i].reply = messagePair.second;
739 // Now that reply is assigned a channel, correct reply to channel association
740 // previously set in queueRequest.
741 channels[i].reply->d_func()->connectionChannel = &channels[i];
742}
743
752
754{
756 return highPriorityQueue.last().second;
758 return lowPriorityQueue.last().second;
759 return nullptr;
760}
761
762// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
764{
765 // return fast if there is nothing to pipeline
767 return;
768
769 int i = indexOf(socket);
770
771 // return fast if there was no reply right now processed
772 if (channels[i].reply == nullptr)
773 return;
774
775 if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.size() >= defaultRePipelineLength)) {
776 return;
777 }
778
780 return;
781
782 // the current request that is in must already support pipelining
783 if (!channels[i].request.isPipeliningAllowed())
784 return;
785
786 // the current request must be a idempotent (right now we only check GET)
787 if (channels[i].request.operation() != QHttpNetworkRequest::Get)
788 return;
789
790 // check if socket is connected
792 return;
793
794 // check for resendCurrent
795 if (channels[i].resendCurrent)
796 return;
797
798 // we do not like authentication stuff
799 // ### make sure to be OK with this in later releases
800 if (!channels[i].authenticator.isNull()
803 return;
804 if (!channels[i].proxyAuthenticator.isNull()
807 return;
808
809 // must be in ReadingState or WaitingState
812 return;
813
814 int lengthBefore;
815 while (!highPriorityQueue.isEmpty()) {
816 lengthBefore = channels[i].alreadyPipelinedRequests.size();
818
819 if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
821 return;
822 }
823
824 if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
825 break; // did not process anything, now do the low prio queue
826 }
827
828 while (!lowPriorityQueue.isEmpty()) {
829 lengthBefore = channels[i].alreadyPipelinedRequests.size();
831
832 if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
834 return;
835 }
836
837 if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
838 break; // did not process anything
839 }
840
841
843}
844
845// returns true when the processing of a queue has been done
847{
848 if (queue.isEmpty())
849 return true;
850
851 for (int i = queue.size() - 1; i >= 0; --i) {
852 HttpMessagePair messagePair = queue.at(i);
853 const QHttpNetworkRequest &request = messagePair.first;
854
855 // we currently do not support pipelining if HTTP authentication is used
856 if (!request.url().userInfo().isEmpty())
857 continue;
858
859 // take only GET requests
860 if (request.operation() != QHttpNetworkRequest::Get)
861 continue;
862
863 if (!request.isPipeliningAllowed())
864 continue;
865
866 // remove it from the queue
867 queue.takeAt(i);
868 // we modify the queue we iterate over here, but since we return from the function
869 // afterwards this is fine.
870
871 // actually send it
872 if (!messagePair.second->d_func()->requestIsPrepared)
873 prepareRequest(messagePair);
874 channel.pipelineInto(messagePair);
875
876 // return false because we processed something and need to process again
877 return false;
878 }
879
880 // return true, the queue has been processed and not changed
881 return true;
882}
883
884
886{
887 QString errorString;
888 switch (errorCode) {
891 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(peerName);
892 break;
893 }
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 += ": "_L1 + 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 // is the reply inside the H2 pipeline of this channel already?
991 const auto foundReply = [reply](const HttpMessagePair &pair) {
992 return pair.second == reply;
993 };
994 auto &seq = channels[i].h2RequestsToSend;
995 const auto end = seq.cend();
996 auto it = std::find_if(seq.cbegin(), end, foundReply);
997 if (it != end) {
998 seq.erase(it);
999 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1000 return;
1001 }
1002 }
1003 // remove from the high priority queue
1004 if (!highPriorityQueue.isEmpty()) {
1005 for (int j = highPriorityQueue.size() - 1; j >= 0; --j) {
1006 HttpMessagePair messagePair = highPriorityQueue.at(j);
1007 if (messagePair.second == reply) {
1009 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1010 return;
1011 }
1012 }
1013 }
1014 // remove from the low priority queue
1015 if (!lowPriorityQueue.isEmpty()) {
1016 for (int j = lowPriorityQueue.size() - 1; j >= 0; --j) {
1017 HttpMessagePair messagePair = lowPriorityQueue.at(j);
1018 if (messagePair.second == reply) {
1020 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
1021 return;
1022 }
1023 }
1024 }
1025}
1026
1027
1028
1029// This function must be called from the event loop. The only
1030// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
1031// although it is called _q_startNextRequest, it will actually start multiple requests when possible
1033{
1034 // If there is no network layer state decided we should not start any new requests.
1036 return;
1037
1038 // If the QHttpNetworkConnection is currently paused then bail out immediately
1039 if (state == PausedState)
1040 return;
1041
1042 //resend the necessary ones.
1043 for (int i = 0; i < activeChannelCount; ++i) {
1045 if (!channels[i].socket
1047 if (!channels[i].ensureConnection())
1048 continue;
1049 }
1050 channels[i].resendCurrent = false;
1051
1052 // if this is not possible, error will be emitted and connection terminated
1053 if (!channels[i].resetUploadData())
1054 continue;
1056 }
1057 }
1058
1059 // dequeue new ones
1060
1061 switch (connectionType) {
1063 // return fast if there is nothing to do
1065 return;
1066
1067 // try to get a free AND connected socket
1068 for (int i = 0; i < activeChannelCount; ++i) {
1069 if (channels[i].socket) {
1070 if (!channels[i].reply && !channels[i].isSocketBusy()
1075 }
1076 }
1077 }
1078 break;
1079 }
1082 if (channels[0].h2RequestsToSend.isEmpty() && !channels[0].reply
1084 return;
1085 }
1086
1087 if (networkLayerState == IPv4)
1089 else if (networkLayerState == IPv6)
1092 if (auto *s = channels[0].socket; s
1094 && !channels[0].pendingEncrypt) {
1095 if (channels[0].h2RequestsToSend.size()) {
1096 channels[0].sendRequest();
1097 } else if (!channels[0].reply && !channels[0].switchedToHttp2) {
1098 // This covers an edge-case where we're already connected and the "connected"
1099 // signal was already sent, but we didn't have any request available at the time,
1100 // so it was missed. As such we need to dequeue a request and send it now that we
1101 // have one.
1103 channels[0].sendRequest();
1104 }
1105 }
1106 break;
1107 }
1108 }
1109
1110 // try to push more into all sockets
1111 // ### FIXME we should move this to the beginning of the function
1112 // as soon as QtWebkit is properly using the pipelining
1113 // (e.g. not for XMLHttpRequest or the first page load)
1114 // ### FIXME we should also divide the requests more even
1115 // on the connected sockets
1116 //tryToFillPipeline(socket);
1117 // return fast if there is nothing to pipeline
1119 return;
1120 for (int i = 0; i < activeChannelCount; i++) {
1121 if (channels[i].socket
1125 }
1126 }
1127
1128 // If there is not already any connected channels we need to connect a new one.
1129 // We do not pair the channel with the request until we know if it is
1130 // connected or not. This is to reuse connected channels before we connect new once.
1131 int queuedRequests = highPriorityQueue.size() + lowPriorityQueue.size();
1132
1133 // in case we have in-flight preconnect requests and normal requests,
1134 // we only need one socket for each (preconnect, normal request) pair
1135 int neededOpenChannels = queuedRequests;
1136 if (preConnectRequests > 0) {
1137 int normalRequests = queuedRequests - preConnectRequests;
1138 neededOpenChannels = qMax(normalRequests, preConnectRequests);
1139 }
1140
1141 if (neededOpenChannels <= 0)
1142 return;
1143
1144 QVarLengthArray<int> channelsToConnect;
1145
1146 // use previously used channels first
1147 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1148 if (!channels[i].socket)
1149 continue;
1150
1152 if ((QSocketAbstraction::socketState(channels[i].socket) == State::ConnectingState)
1153 || (QSocketAbstraction::socketState(channels[i].socket) == State::HostLookupState)
1154 || channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState"
1155 neededOpenChannels--;
1156 continue;
1157 }
1158
1159 if (!channels[i].reply && !channels[i].isSocketBusy()
1160 && (QSocketAbstraction::socketState(channels[i].socket) == State::UnconnectedState)) {
1161 channelsToConnect.push_back(i);
1162 neededOpenChannels--;
1163 }
1164 }
1165
1166 // use other channels
1167 for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
1168 if (channels[i].socket)
1169 continue;
1170
1171 channelsToConnect.push_back(i);
1172 neededOpenChannels--;
1173 }
1174
1175 auto channelToConnectSpan = QSpan{channelsToConnect};
1176 while (!channelToConnectSpan.isEmpty()) {
1177 const int channel = channelToConnectSpan.front();
1178 channelToConnectSpan = channelToConnectSpan.sliced(1);
1179
1180 if (networkLayerState == IPv4)
1182 else if (networkLayerState == IPv6)
1184
1186 }
1187}
1188
1189
1191{
1192 for (int i = 0 ; i < activeChannelCount; ++i) {
1193 if (channels[i].reply == reply) {
1194 // emulate a readyRead() from the socket
1196 return;
1197 }
1198 }
1199}
1200
1201
1202
1203// The first time we start the connection is used we do not know if we
1204// should use IPv4 or IPv6. So we start a hostlookup to figure this out.
1205// Later when we do the connection the socket will not need to do another
1206// lookup as then the hostinfo will already be in the cache.
1208{
1210
1211 // check if we already now can decide if this is IPv4 or IPv6
1212 QString lookupHost = hostName;
1213#ifndef QT_NO_NETWORKPROXY
1215 lookupHost = networkProxy.hostName();
1217 lookupHost = channels[0].proxy.hostName();
1218 }
1219#endif
1220 QHostAddress temp;
1221 if (temp.setAddress(lookupHost)) {
1222 const QAbstractSocket::NetworkLayerProtocol protocol = temp.protocol();
1223 if (protocol == QAbstractSocket::IPv4Protocol) {
1225 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1226 return;
1227 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1229 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1230 return;
1231 }
1232 } else {
1233 int hostLookupId;
1234 bool immediateResultValid = false;
1235 QHostInfo hostInfo = qt_qhostinfo_lookup(lookupHost,
1236 this->q_func(),
1238 &immediateResultValid,
1239 &hostLookupId);
1240 if (immediateResultValid) {
1241 _q_hostLookupFinished(hostInfo);
1242 }
1243 }
1244}
1245
1246
1248{
1249 bool bIpv4 = false;
1250 bool bIpv6 = false;
1251 bool foundAddress = false;
1253 return;
1254
1255 const auto addresses = info.addresses();
1256 for (const QHostAddress &address : addresses) {
1257 const QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
1258 if (protocol == QAbstractSocket::IPv4Protocol) {
1259 if (!foundAddress) {
1260 foundAddress = true;
1261 delayIpv4 = false;
1262 }
1263 bIpv4 = true;
1264 } else if (protocol == QAbstractSocket::IPv6Protocol) {
1265 if (!foundAddress) {
1266 foundAddress = true;
1267 delayIpv4 = true;
1268 }
1269 bIpv6 = true;
1270 }
1271 }
1272
1273 if (bIpv4 && bIpv6)
1275 else if (bIpv4) {
1277 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1278 } else if (bIpv6) {
1280 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1281 } else {
1282 auto lookupError = QNetworkReply::HostNotFoundError;
1283#ifndef QT_NO_NETWORKPROXY
1284 // if the proxy can lookup hostnames, all hostname lookups except for the lookup of the
1285 // proxy hostname are delegated to the proxy.
1286 auto proxyCapabilities = networkProxy.capabilities() | channels[0].proxy.capabilities();
1287 if (proxyCapabilities & QNetworkProxy::HostNameLookupCapability)
1289#endif
1290 if (dequeueRequest(channels[0].socket)) {
1291 emitReplyError(channels[0].socket, channels[0].reply, lookupError);
1295 for (const HttpMessagePair &h2Pair : std::as_const(channels[0].h2RequestsToSend)) {
1296 // emit error for all replies
1297 QHttpNetworkReply *currentReply = h2Pair.second;
1298 Q_ASSERT(currentReply);
1299 emitReplyError(channels[0].socket, currentReply, lookupError);
1300 }
1301 } else {
1302 // We can end up here if a request has been aborted or otherwise failed (e.g. timeout)
1303 // before the host lookup was finished.
1304 qDebug("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
1305 " could not de-queue request, failed to report HostNotFoundError");
1307 }
1308 }
1309}
1310
1311
1312// This will be used if the host lookup found both and Ipv4 and
1313// Ipv6 address. Then we will start up two connections and pick
1314// the network layer of the one that finish first. The second
1315// connection will then be disconnected.
1317{
1318 if (activeChannelCount > 1) {
1319 // At this time all channels should be unconnected.
1320 Q_ASSERT(!channels[0].isSocketBusy());
1321 Q_ASSERT(!channels[1].isSocketBusy());
1322
1324
1327
1328 int timeout = 300;
1330 if (delayIpv4)
1332 else
1334 } else {
1338 }
1339}
1340
1342{
1343 for (int i = 0 ; i < activeChannelCount; ++i) {
1344 if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
1345 channels[i].close();
1346 }
1347 }
1348}
1349
1357
1359 quint16 port, bool encrypt, bool isLocalSocket, QObject *parent,
1361 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt, isLocalSocket,
1362 connectionType)), parent)
1363{
1365 d->init();
1369 }
1370}
1371
1375
1377{
1378 Q_D(const QHttpNetworkConnection);
1379 return d->hostName;
1380}
1381
1383{
1384 Q_D(const QHttpNetworkConnection);
1385 return d->port;
1386}
1387
1393
1395{
1397 d->fillHttp2Queue();
1398}
1399
1401{
1402 Q_D(const QHttpNetworkConnection);
1403 return d->encrypt;
1404}
1405
1407{
1408 return d_func()->channels;
1409}
1410
1411#ifndef QT_NO_NETWORKPROXY
1413{
1415 d->networkProxy = networkProxy;
1416 // update the authenticator
1417 if (!d->networkProxy.user().isEmpty()) {
1418 for (int i = 0; i < d->channelCount; ++i) {
1419 d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
1420 d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
1421 }
1422 }
1423}
1424
1426{
1427 Q_D(const QHttpNetworkConnection);
1428 return d->networkProxy;
1429}
1430
1432{
1434 for (int i = 0; i < d->channelCount; ++i)
1435 d->channels[i].setProxy(networkProxy);
1436}
1437
1439{
1440 Q_D(const QHttpNetworkConnection);
1441 return d->channels[0].proxy;
1442}
1443#endif
1444
1446{
1447 Q_D(const QHttpNetworkConnection);
1448 return d->connectionType;
1449}
1450
1456
1458{
1459 Q_D(const QHttpNetworkConnection);
1460 return d->http2Parameters;
1461}
1462
1468
1469// SSL support below
1470#ifndef QT_NO_SSL
1472{
1474 if (!d->encrypt)
1475 return;
1476
1477 // set the config on all channels
1478 for (int i = 0; i < d->activeChannelCount; ++i)
1479 d->channels[i].setSslConfiguration(config);
1480}
1481
1482std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext() const
1483{
1484 Q_D(const QHttpNetworkConnection);
1485 return d->sslContext;
1486}
1487
1488void QHttpNetworkConnection::setSslContext(std::shared_ptr<QSslContext> context)
1489{
1491 d->sslContext = std::move(context);
1492}
1493
1495{
1497 if (!d->encrypt)
1498 return;
1499
1500 if (channel == -1) { // ignore for all channels
1501 // We need to ignore for all channels, even the ones that are not in use just in case they
1502 // will be in the future.
1503 for (int i = 0; i < d->channelCount; ++i) {
1504 d->channels[i].ignoreSslErrors();
1505 }
1506
1507 } else {
1508 d->channels[channel].ignoreSslErrors();
1509 }
1510}
1511
1512void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
1513{
1515 if (!d->encrypt)
1516 return;
1517
1518 if (channel == -1) { // ignore for all channels
1519 // We need to ignore for all channels, even the ones that are not in use just in case they
1520 // will be in the future.
1521 for (int i = 0; i < d->channelCount; ++i) {
1522 d->channels[i].ignoreSslErrors(errors);
1523 }
1524
1525 } else {
1526 d->channels[channel].ignoreSslErrors(errors);
1527 }
1528}
1529
1530#endif //QT_NO_SSL
1531
1533{
1534 d_func()->preConnectRequests--;
1535}
1536
1538{
1539 Q_D(const QHttpNetworkConnection);
1540 return d->peerVerifyName;
1541}
1542
1544{
1546 d->peerVerifyName = peerName;
1547}
1548
1550{
1552
1553 if (isOnline) {
1554 // If we did not have any 'isOffline' previously - well, good
1555 // to know, we are 'online' apparently.
1556 return;
1557 }
1558
1559 for (int i = 0; i < d->activeChannelCount; i++) {
1560 auto &channel = d->channels[i];
1561 channel.emitFinishedWithError(QNetworkReply::TemporaryNetworkFailureError, "Temporary network failure.");
1562 channel.close();
1563 }
1564
1565 // We don't care, this connection is broken from our POV.
1566 d->connectionMonitor.stopMonitoring();
1567}
1568
1569#ifndef QT_NO_NETWORKPROXY
1570// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
1571// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
1572// e.g. it is for SOCKS proxies which require authentication.
1574{
1575 // Also pause the connection because socket notifiers may fire while an user
1576 // dialog is displaying
1580 && (chan->switchedToHttp2 || chan->h2RequestsToSend.size() > 0))
1582 // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
1583 // but that does not matter because the signal will ultimately be emitted
1584 // by the QNetworkAccessManager.
1585 Q_ASSERT(chan->h2RequestsToSend.size() > 0);
1586 reply = chan->h2RequestsToSend.cbegin().value().second;
1587 } else { // HTTP
1588 reply = chan->reply;
1589 }
1590
1591 Q_ASSERT(reply);
1592 emit reply->proxyAuthenticationRequired(proxy, auth);
1594 int i = indexOf(chan->socket);
1595 copyCredentials(i, auth, true);
1596}
1597#endif
1598
1599
1601
1602#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:398
bool isEmpty() const noexcept
Definition qlist.h:402
T & last()
Definition qlist.h:649
void removeAt(qsizetype i)
Definition qlist.h:591
const_reference at(qsizetype i) const noexcept
Definition qlist.h:447
value_type takeLast()
Definition qlist.h:568
void prepend(rvalue_ref t)
Definition qlist.h:474
void clear()
Definition qlist.h:435
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:1453
const_iterator cend() const
Definition qmap.h:1331
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
QByteArray toLocal8Bit() const &
Definition qstring.h:638
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
#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:165
#define qWarning
Definition qlogging.h:167
#define qFatal
Definition qlogging.h:169
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
#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...