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
qhttpnetworkconnectionchannel.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
8#include "private/qnoncontiguousbytedevice_p.h"
9
10#include <qdebug.h>
11
12#include <private/qhttp2protocolhandler_p.h>
13#include <private/qhttpprotocolhandler_p.h>
14#include <private/http2protocol_p.h>
15#include <private/qsocketabstraction_p.h>
16
17#ifndef QT_NO_SSL
18# include <private/qsslsocket_p.h>
19# include <QtNetwork/qsslkey.h>
20# include <QtNetwork/qsslcipher.h>
21#endif
22
23#include "private/qnetconmonitor_p.h"
24
25#include <memory>
26#include <utility>
27
29
30namespace
31{
32
34{
35public:
37 ~ProtocolHandlerDeleter() { delete handler; }
38private:
39 QAbstractProtocolHandler *handler = nullptr;
40};
41
42}
43
44// TODO: Put channel specific stuff here so it does not pollute qhttpnetworkconnection.cpp
45
46// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP
47// connection times out)
48// We use 3 because we can get a _q_error 3 times depending on the timing:
49static const int reconnectAttemptsDefault = 3;
50
53 , ssl(false)
54 , isInitialized(false)
55 , state(IdleState)
56 , reply(nullptr)
57 , written(0)
58 , bytesTotal(0)
59 , resendCurrent(false)
60 , lastStatus(0)
61 , pendingEncrypt(false)
62 , reconnectAttempts(reconnectAttemptsDefault)
63 , authenticationCredentialsSent(false)
64 , proxyCredentialsSent(false)
65 , protocolHandler(nullptr)
66#ifndef QT_NO_SSL
67 , ignoreAllSslErrors(false)
68#endif
69 , pipeliningSupported(PipeliningSupportUnknown)
70 , networkLayerPreference(QAbstractSocket::AnyIPProtocol)
72{
73 // Inlining this function in the header leads to compiler error on
74 // release-armv5, on at least timebox 9.2 and 10.1.
75}
76
78{
79#ifndef QT_NO_SSL
80 if (connection->d_func()->encrypt)
81 socket = new QSslSocket;
82 else if (connection->d_func()->isLocalSocket)
83 socket = new QLocalSocket;
84 else
85 socket = new QTcpSocket;
86#else
87 socket = new QTcpSocket;
88#endif
89#ifndef QT_NO_NETWORKPROXY
90 // Set by QNAM anyway, but let's be safe here
91 if (auto s = qobject_cast<QAbstractSocket *>(socket))
93#endif
94
95 // After some back and forth in all the last years, this is now a DirectConnection because otherwise
96 // the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers
97 // which behave slightly differently on Windows vs Linux
104
105
106 QSocketAbstraction::visit([this](auto *socket){
107 using SocketType = std::remove_pointer_t<decltype(socket)>;
108 QObject::connect(socket, &SocketType::connected,
111
112 // The disconnected() and error() signals may already come
113 // while calling connectToHost().
114 // In case of a cached hostname or an IP this
115 // will then emit a signal to the user of QNetworkReply
116 // but cannot be caught because the user did not have a chance yet
117 // to connect to QNetworkReply's signals.
118 QObject::connect(socket, &SocketType::disconnected,
121 if constexpr (std::is_same_v<SocketType, QAbstractSocket>) {
125 } else if constexpr (std::is_same_v<SocketType, QLocalSocket>) {
126 auto convertAndForward = [this](QLocalSocket::LocalSocketError error) {
128 };
129 QObject::connect(socket, &SocketType::errorOccurred,
130 this, std::move(convertAndForward),
132 }
133 }, socket);
134
135
136
137#ifndef QT_NO_NETWORKPROXY
138 if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
142 }
143#endif
144
145#ifndef QT_NO_SSL
146 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
147 if (sslSocket) {
148 // won't be a sslSocket if encrypt is false
161
163 sslSocket->ignoreSslErrors();
164
166 sslSocket->ignoreSslErrors(ignoreSslErrorsList);
167
169 sslSocket->setSslConfiguration(*sslConfiguration);
170 } else {
171#endif // !QT_NO_SSL
173 protocolHandler.reset(new QHttpProtocolHandler(this));
174#ifndef QT_NO_SSL
175 }
176#endif
177
178#ifndef QT_NO_NETWORKPROXY
179 if (auto *s = qobject_cast<QAbstractSocket *>(socket);
181 s->setProxy(proxy);
182 }
183#endif
184 isInitialized = true;
185}
186
187
189{
191 return;
192
193 if (!socket)
197 else
199
200 // pendingEncrypt must only be true in between connected and encrypted states
201 pendingEncrypt = false;
202
203 if (socket) {
204 // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
205 // there is no socket yet.
206 socket->close();
207 }
208}
209
210
212{
213 if (!socket)
217 else
219
220 // pendingEncrypt must only be true in between connected and encrypted states
221 pendingEncrypt = false;
222
223 if (socket) {
224 // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
225 // there is no socket yet.
226 auto callAbort = [](auto *s) {
227 s->abort();
228 };
230 }
231}
232
233
239
240/*
241 * Invoke "protocolHandler->sendRequest" using a queued connection.
242 * It's used to return to the event loop before invoking sendRequest when
243 * there's a very real chance that the request could have been aborted
244 * (i.e. after having emitted 'encrypted').
245 */
254
260
266
267// called when unexpectedly reading a -1 or when data is expected but socket is closed
269{
271 if (reconnectAttempts <= 0) {
272 // too many errors reading/receiving/parsing the status, close the socket and emit error
274 close();
275 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
277 reply = nullptr;
278 if (protocolHandler)
279 protocolHandler->setReply(nullptr);
282 } else {
284 reply->d_func()->clear();
285 reply->d_func()->connection = connection;
286 reply->d_func()->connectionChannel = this;
288 }
289}
290
292{
293 if (!isInitialized)
294 init();
295
297
298 // resend this request after we receive the disconnected signal
299 // If !socket->isOpen() then we have already called close() on the socket, but there was still a
300 // pending connectToHost() for which we hadn't seen a connected() signal, yet. The connected()
301 // has now arrived (as indicated by socketState != ClosingState), but we cannot send anything on
302 // such a socket anymore.
303 if (socketState == QAbstractSocket::ClosingState ||
304 (socketState != QAbstractSocket::UnconnectedState && !socket->isOpen())) {
305 if (reply)
306 resendCurrent = true;
307 return false;
308 }
309
310 // already trying to connect?
311 if (socketState == QAbstractSocket::HostLookupState ||
312 socketState == QAbstractSocket::ConnectingState) {
313 return false;
314 }
315
316 // make sure that this socket is in a connected state, if not initiate
317 // connection to the host.
318 if (socketState != QAbstractSocket::ConnectedState) {
319 // connect to the host if not already connected.
322
323 // reset state
326 proxyCredentialsSent = false;
329 priv->hasFailed = false;
332 priv->hasFailed = false;
333
334 // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
335 // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
336 // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
337 // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
338 // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
339 // the phase is reset to Start.
341 if (priv && priv->phase == QAuthenticatorPrivate::Done)
344 if (priv && priv->phase == QAuthenticatorPrivate::Done)
346
347 QString connectHost = connection->d_func()->hostName;
348 quint16 connectPort = connection->d_func()->port;
349
350 QHttpNetworkReply *potentialReply = connection->d_func()->predictNextRequestsReply();
351 if (potentialReply) {
352 QMetaObject::invokeMethod(potentialReply, "socketStartedConnecting", Qt::QueuedConnection);
353 } else if (!h2RequestsToSend.isEmpty()) {
354 QMetaObject::invokeMethod(std::as_const(h2RequestsToSend).first().second, "socketStartedConnecting", Qt::QueuedConnection);
355 }
356
357#ifndef QT_NO_NETWORKPROXY
358 // HTTPS always use transparent proxy.
359 if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
360 connectHost = connection->d_func()->networkProxy.hostName();
361 connectPort = connection->d_func()->networkProxy.port();
362 }
363 if (auto *abSocket = qobject_cast<QAbstractSocket *>(socket);
364 abSocket && abSocket->proxy().type() == QNetworkProxy::HttpProxy) {
365 // Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
367 // ensureConnection is called before any request has been assigned, but can also be
368 // called again if reconnecting
369 if (request.url().isEmpty()) {
373 && !h2RequestsToSend.isEmpty())) {
374 value = std::as_const(h2RequestsToSend).first().first.headerField("user-agent");
375 } else {
376 value = connection->d_func()->predictNextRequest().headerField("user-agent");
377 }
378 } else {
379 value = request.headerField("user-agent");
380 }
381 if (!value.isEmpty()) {
382 QNetworkProxy proxy(abSocket->proxy());
383 auto h = proxy.headers();
385 proxy.setHeaders(std::move(h));
386 abSocket->setProxy(proxy);
387 }
388 }
389#endif
390 if (ssl) {
391#ifndef QT_NO_SSL
392 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
393
394 // check whether we can re-use an existing SSL session
395 // (meaning another socket in this connection has already
396 // performed a full handshake)
397 if (auto ctx = connection->sslContext())
398 QSslSocketPrivate::checkSettingSslContext(sslSocket, std::move(ctx));
399
400 sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName);
401 sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
403 sslSocket->ignoreSslErrors();
404 sslSocket->ignoreSslErrors(ignoreSslErrorsList);
405
406 // limit the socket read buffer size. we will read everything into
407 // the QHttpNetworkReply anyway, so let's grow only that and not
408 // here and there.
409 sslSocket->setReadBufferSize(64*1024);
410#else
411 // Need to dequeue the request so that we can emit the error.
412 if (!reply)
413 connection->d_func()->dequeueRequest(socket);
414 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
415#endif
416 } else {
417 // In case of no proxy we can use the Unbuffered QTcpSocket
418#ifndef QT_NO_NETWORKPROXY
419 if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
422#endif
423 if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
424 s->connectToHost(connectHost, connectPort,
427 // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
428 s->setReadBufferSize(1 * 1024);
429 } else if (auto *s = qobject_cast<QLocalSocket *>(socket)) {
430 s->connectToServer(connectHost);
431 }
432#ifndef QT_NO_NETWORKPROXY
433 } else {
434 auto *s = qobject_cast<QAbstractSocket *>(socket);
435 Q_ASSERT(s);
436 // limit the socket read buffer size. we will read everything into
437 // the QHttpNetworkReply anyway, so let's grow only that and not
438 // here and there.
439 s->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
440 s->setReadBufferSize(64 * 1024);
441 }
442#endif
443 }
444 return false;
445 }
446
447 // This code path for ConnectedState
448 if (pendingEncrypt) {
449 // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up
450 // and corrupt the things sent to the server.
451 return false;
452 }
453
454 return true;
455}
456
458{
460
461 if (!reply) {
462 qWarning("QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.io/");
463 return;
464 }
465
466 // For clear text HTTP/2 we tried to upgrade from HTTP/1.1 to HTTP/2; for
467 // ConnectionTypeHTTP2Direct we can never be here in case of failure
468 // (after an attempt to read HTTP/1.1 as HTTP/2 frames) or we have a normal
469 // HTTP/2 response and thus can skip this test:
471 && !ssl && !switchedToHttp2) {
473 switchedToHttp2 = true;
474 protocolHandler->setReply(nullptr);
475
476 // As allDone() gets called from the protocol handler, it's not yet
477 // safe to delete it. There is no 'deleteLater', since
478 // QAbstractProtocolHandler is not a QObject. Instead we do this
479 // trick with ProtocolHandlerDeleter, a QObject-derived class.
480 // These dances below just make it somewhat exception-safe.
481 // 1. Create a new owner:
482 QAbstractProtocolHandler *oldHandler = protocolHandler.get();
483 auto deleter = std::make_unique<ProtocolHandlerDeleter>(oldHandler);
484 // 2. Retire the old one:
485 Q_UNUSED(protocolHandler.release());
486 // 3. Call 'deleteLater':
487 deleter->deleteLater();
488 // 3. Give up the ownerthip:
489 Q_UNUSED(deleter.release());
490
492 protocolHandler.reset(new QHttp2ProtocolHandler(this));
494 QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection);
496 // If we only had one request sent with H2 allowed, we may fail to send
497 // a client preface and SETTINGS, which is required by RFC 7540, 3.2.
498 QMetaObject::invokeMethod(h2c, "ensureClientPrefaceSent", Qt::QueuedConnection);
499 return;
500 } else {
501 // Ok, whatever happened, we do not try HTTP/2 anymore ...
503 connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
504 }
505 }
506
507 // while handling 401 & 407, we might reset the status code, so save this.
508 bool emitFinished = reply->d_func()->shouldEmitSignals();
509 bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
511
512 handleStatus();
513 // handleStatus() might have removed the reply because it already called connection->emitReplyError()
514
515 // queue the finished signal, this is required since we might send new requests from
516 // slot connected to it. The socket will not fire readyRead signal, if we are already
517 // in the slot connected to readyRead
518 if (reply && emitFinished)
520
521
522 // reset the reconnection attempts after we receive a complete reply.
523 // in case of failures, each channel will attempt two reconnects before emitting error.
525
526 // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
529
530 // if it does not need to be sent again we can set it to 0
531 // the previous code did not do that and we had problems with accidental re-sending of a
532 // finished request.
533 // Note that this may trigger a segfault at some other point. But then we can fix the underlying
534 // problem.
535 if (!resendCurrent) {
537 reply = nullptr;
538 protocolHandler->setReply(nullptr);
539 }
540
541 // move next from pipeline to current request
544 // move the pipelined ones back to the main queue
546 close();
547 } else {
548 // there were requests pipelined in and we can continue
550
551 request = messagePair.first;
552 reply = messagePair.second;
553 protocolHandler->setReply(messagePair.second);
555 resendCurrent = false;
556
557 written = 0; // message body, excluding the header, irrelevant here
558 bytesTotal = 0; // message body total, excluding the header, irrelevant here
559
560 // pipeline even more
561 connection->d_func()->fillPipeline(socket);
562
563 // continue reading
564 //_q_receiveReply();
565 // this was wrong, allDone gets called from that function anyway.
566 }
567 } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
568 // this is weird. we had nothing pipelined but still bytes available. better close it.
569 close();
570
572 } else if (alreadyPipelinedRequests.isEmpty()) {
573 if (connectionCloseEnabled)
575 close();
576 if (qobject_cast<QHttpNetworkConnection*>(connection))
578 }
579}
580
582{
584 // detect HTTP Pipelining support
585 QByteArray serverHeaderField;
586 if (
587 // check for HTTP/1.1
588 (reply->majorVersion() == 1 && reply->minorVersion() == 1)
589 // check for not having connection close
590 && (!reply->d_func()->isConnectionCloseEnabled())
591 // check if it is still connected
593 // check for broken servers in server reply header
594 // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
595 && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
596 && (!serverHeaderField.contains("Microsoft-IIS/5."))
597 && (!serverHeaderField.contains("Netscape-Enterprise/3."))
598 // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
599 && (!serverHeaderField.contains("WebLogic"))
600 && (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
601 ) {
603 } else {
605 }
606}
607
608// called when the connection broke and we need to queue some pipelined requests again
610{
611 for (int i = 0; i < alreadyPipelinedRequests.size(); i++)
612 connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
614
615 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
616 // this function is called from _q_disconnected which is called because
617 // of ~QHttpNetworkConnectionPrivate
618 if (qobject_cast<QHttpNetworkConnection*>(connection))
620}
621
623{
626
627 int statusCode = reply->statusCode();
628 bool resend = false;
629
630 switch (statusCode) {
631 case 301:
632 case 302:
633 case 303:
634 case 305:
635 case 307:
636 case 308: {
637 // Parse the response headers and get the "location" url
638 QUrl redirectUrl = connection->d_func()->parseRedirectResponse(socket, reply);
639 if (redirectUrl.isValid())
640 reply->setRedirectUrl(redirectUrl);
641
642 if ((statusCode == 307 || statusCode == 308) && !resetUploadData()) {
643 // Couldn't reset the upload data, which means it will be unable to POST the data -
644 // this would lead to a long wait until it eventually failed and then retried.
645 // Instead of doing that we fail here instead, resetUploadData will already have emitted
646 // a ContentReSendError, so we're done.
647 } else if (qobject_cast<QHttpNetworkConnection *>(connection)) {
649 }
650 break;
651 }
652 case 401: // auth required
653 case 407: // proxy auth required
654 if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
655 if (resend) {
656 if (!resetUploadData())
657 break;
658
659 reply->d_func()->eraseData();
660
662 // this does a re-send without closing the connection
663 resendCurrent = true;
665 } else {
666 // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
669 }
670 } else {
671 //authentication cancelled, close the channel.
672 close();
673 }
674 } else {
677 QNetworkReply::NetworkError errorCode = (statusCode == 407)
680 reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
681 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
682 }
683 break;
684 default:
685 if (qobject_cast<QHttpNetworkConnection*>(connection))
687 }
688}
689
691{
692 if (!reply) {
693 //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
694 return false;
695 }
697 || switchedToHttp2) {
698 // The else branch doesn't make any sense for HTTP/2, since 1 channel is multiplexed into
699 // many streams. And having one stream fail to reset upload data should not completely close
700 // the channel. Handled in the http2 protocol handler.
701 } else if (QNonContiguousByteDevice *uploadByteDevice = request.uploadByteDevice()) {
702 if (!uploadByteDevice->reset()) {
703 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
704 return false;
705 }
706 written = 0;
707 }
708 return true;
709}
710
711#ifndef QT_NO_NETWORKPROXY
712
714{
715 if (auto *s = qobject_cast<QAbstractSocket *>(socket))
716 s->setProxy(networkProxy);
717
718 proxy = networkProxy;
719}
720
721#endif
722
723#ifndef QT_NO_SSL
724
726{
727 if (socket)
728 static_cast<QSslSocket *>(socket)->ignoreSslErrors();
729
730 ignoreAllSslErrors = true;
731}
732
733
734void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors)
735{
736 if (socket)
737 static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors);
738
739 ignoreSslErrorsList = errors;
740}
741
752
753#endif
754
756{
757 // this is only called for simple GET
758
759 QHttpNetworkRequest &request = pair.first;
760 QHttpNetworkReply *reply = pair.second;
761 reply->d_func()->clear();
762 reply->d_func()->connection = connection;
763 reply->d_func()->connectionChannel = this;
764 reply->d_func()->autoDecompress = request.d->autoDecompress;
765 reply->d_func()->pipeliningUsed = true;
766
767#ifndef QT_NO_NETWORKPROXY
769 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
770#else
772#endif
773
775
776 // pipelineFlush() needs to be called at some point afterwards
777}
778
780{
781 if (pipeline.isEmpty())
782 return;
783
784 // The goal of this is so that we have everything in one TCP packet.
785 // For the Unbuffered QTcpSocket this is manually needed, the buffered
786 // QTcpSocket does it automatically.
787 // Also, sometimes the OS does it for us (Nagle's algorithm) but that
788 // happens only sometimes.
790 pipeline.clear();
791}
792
793
795{
797 close();
798 if (reply)
799 resendCurrent = true;
800 if (qobject_cast<QHttpNetworkConnection*>(connection))
802}
803
805{
807 if (reply)
808 resendCurrent = true;
809 if (qobject_cast<QHttpNetworkConnection*>(connection))
811}
812
817
822
827
832
834{
835 Q_UNUSED(bytes);
836 if (ssl) {
837 // In the SSL case we want to send data from encryptedBytesWritten signal since that one
838 // is the one going down to the actual network, not only into some SSL buffer.
839 return;
840 }
841
842 // bytes have been written to the socket. write even more of them :)
843 if (isSocketWriting())
844 sendRequest();
845 // otherwise we do nothing
846}
847
849{
853 return;
854 }
855
856 // read the available data before closing (also done in _q_error for other codepaths)
858 if (reply) {
861 }
863 // re-sending request because the socket was in ClosingState
865 }
868 // If nothing was in a pipeline, no need in calling
869 // _q_startNextRequest (which it does):
871 }
872
873 pendingEncrypt = false;
874}
875
876
878{
879 // For the Happy Eyeballs we need to check if this is the first channel to connect.
880 if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::HostLookupPending || connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4or6) {
881 if (connection->d_func()->delayedConnectionTimer.isActive())
882 connection->d_func()->delayedConnectionTimer.stop();
884 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
886 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
887 else {
888 if (absSocket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
889 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
890 else
891 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
892 }
893 connection->d_func()->networkLayerDetected(networkLayerPreference);
894 if (connection->d_func()->activeChannelCount > 1 && !connection->d_func()->encrypt)
896 } else {
898 if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4)
900 || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6)
901 && (networkLayerPreference != QAbstractSocket::IPv6Protocol && !anyProtocol))) {
902 close();
903 // This is the second connection so it has to be closed and we can schedule it for another request.
905 return;
906 }
907 //The connections networkLayerState had already been decided.
908 }
909
910 // improve performance since we get the request sent by the kernel ASAP
911 //absSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
912 // We have this commented out now. It did not have the effect we wanted. If we want to
913 // do this properly, Qt has to combine multiple HTTP requests into one buffer
914 // and send this to the kernel in one syscall and then the kernel immediately sends
915 // it as one TCP packet because of TCP_NODELAY.
916 // However, this code is currently not in Qt, so we rely on the kernel combining
917 // the requests into one TCP packet.
918
919 // not sure yet if it helps, but it makes sense
920 absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
921
923
925 auto connectionPrivate = connection->d_func();
926 if (!connectionPrivate->connectionMonitor.isMonitoring()) {
927 // Now that we have a pair of addresses, we can start monitoring the
928 // connection status to handle its loss properly.
929 if (connectionPrivate->connectionMonitor.setTargets(absSocket->localAddress(), absSocket->peerAddress()))
930 connectionPrivate->connectionMonitor.startMonitoring();
931 }
932 }
933
934 // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
935 //channels[i].reconnectAttempts = 2;
936 if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
937#ifndef QT_NO_SSL
938 if (!connection->sslContext()) {
939 // this socket is making the 1st handshake for this connection,
940 // we need to set the SSL context so new sockets can reuse it
941 if (auto socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(absSocket)))
942 connection->setSslContext(std::move(socketSslContext));
943 }
944#endif
947 protocolHandler.reset(new QHttp2ProtocolHandler(this));
948 if (h2RequestsToSend.size() > 0) {
949 // In case our peer has sent us its settings (window size, max concurrent streams etc.)
950 // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
952 }
953 } else {
955 const bool tryProtocolUpgrade = connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2;
956 if (tryProtocolUpgrade) {
957 // For HTTP/1.1 it's already created and never reset.
958 protocolHandler.reset(new QHttpProtocolHandler(this));
959 }
960 switchedToHttp2 = false;
961
962 if (!reply)
963 connection->d_func()->dequeueRequest(absSocket);
964
965 if (reply) {
966 if (tryProtocolUpgrade) {
967 // Let's augment our request with some magic headers and try to
968 // switch to HTTP/2.
970 }
971 sendRequest();
972 }
973 }
974}
975
977{
979 if (!reply) // No reply object, try to dequeue a request (which is paired with a reply):
980 connection->d_func()->dequeueRequest(localSocket);
981 if (reply)
982 sendRequest();
983}
984
986{
987 if (auto *s = qobject_cast<QAbstractSocket *>(socket))
989 else if (auto *s = qobject_cast<QLocalSocket *>(socket))
991}
992
994{
995 if (!socket)
996 return;
998
999 switch (socketError) {
1002 break;
1005#ifndef QT_NO_NETWORKPROXY
1006 if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl)
1008#endif
1009 break;
1011 // This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
1012 // Depending on timing it can also come three times in a row (first time when we try to write into a closing QSslSocket).
1013 // The reconnectAttempts handling catches the cases where we can re-send the request.
1015 // Not actually an error, it is normal for Keep-Alive connections to close after some time if no request
1016 // is sent on them. No need to error the other replies below. Just bail out here.
1017 // The _q_disconnected will handle the possibly pipelined replies. HTTP/2 is special for now,
1018 // we do not resend, but must report errors if any request is in progress (note, while
1019 // not in its sendRequest(), protocol handler switches the channel to IdleState, thus
1020 // this check is under this condition in 'if'):
1021 if (protocolHandler) {
1024 && switchedToHttp2)) {
1025 auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
1026 h2Handler->handleConnectionClosure();
1027 }
1028 }
1029 return;
1031 // Try to reconnect/resend before sending an error.
1032 // While "Reading" the _q_disconnected() will handle this.
1033 // If we're using ssl then the protocolHandler is not initialized until
1034 // "encrypted" has been emitted, since retrying requires the protocolHandler (asserted)
1035 // we will not try if encryption is not done.
1036 if (!pendingEncrypt && reconnectAttempts-- > 0) {
1038 return;
1039 } else {
1041 }
1043 if (!reply)
1044 break;
1045
1046 if (!reply->d_func()->expectContent()) {
1047 // No content expected, this is a valid way to have the connection closed by the server
1048 // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
1049 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1050 return;
1051 }
1052 if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1053 // There was no content-length header and it's not chunked encoding,
1054 // so this is a valid way to have the connection closed by the server
1055 // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
1056 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1057 return;
1058 }
1059 // ok, we got a disconnect even though we did not expect it
1060 // Try to read everything from the socket before we emit the error.
1061 if (socket->bytesAvailable()) {
1062 // Read everything from the socket into the reply buffer.
1063 // we can ignore the readbuffersize as the data is already
1064 // in memory and we will not receive more data on the socket.
1068 if (!reply) {
1069 // No more reply assigned after the previous call? Then it had been finished successfully.
1073 return;
1074 }
1075 }
1076
1078 } else {
1080 }
1081 break;
1083 // try to reconnect/resend before sending an error.
1086 return;
1087 }
1088 errorCode = QNetworkReply::TimeoutError;
1089 break;
1092 break;
1095 break;
1098 break;
1100 // try to reconnect/resend before sending an error.
1101 if (reconnectAttempts-- > 0) {
1103 return;
1104 }
1106 break;
1108 // try to reconnect/resend before sending an error.
1109 if (reconnectAttempts-- > 0) {
1111 return;
1112 }
1114 break;
1115 default:
1116 // all other errors are treated as NetworkError
1118 break;
1119 }
1120 QPointer<QHttpNetworkConnection> that = connection;
1121 QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1122
1123 // In the HostLookupPending state the channel should not emit the error.
1124 // This will instead be handled by the connection.
1125 if (!connection->d_func()->shouldEmitChannelError(socket))
1126 return;
1127
1128 // emit error for all waiting replies
1129 do {
1130 // First requeue the already pipelined requests for the current failed reply,
1131 // then dequeue pending requests so we can also mark them as finished with error
1132 if (reply)
1134 else
1135 connection->d_func()->dequeueRequest(socket);
1136
1137 if (reply) {
1138 reply->d_func()->errorString = errorString;
1139 reply->d_func()->httpErrorCode = errorCode;
1140 emit reply->finishedWithError(errorCode, errorString);
1141 reply = nullptr;
1142 if (protocolHandler)
1143 protocolHandler->setReply(nullptr);
1144 }
1145 } while (!connection->d_func()->highPriorityQueue.isEmpty()
1146 || !connection->d_func()->lowPriorityQueue.isEmpty());
1147
1150 const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
1151 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1152 // emit error for all replies
1153 QHttpNetworkReply *currentReply = httpMessagePair.second;
1154 currentReply->d_func()->errorString = errorString;
1155 currentReply->d_func()->httpErrorCode = errorCode;
1156 Q_ASSERT(currentReply);
1157 emit currentReply->finishedWithError(errorCode, errorString);
1158 }
1159 }
1160
1161 // send the next request
1162 QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1163
1164 if (that) {
1165 //signal emission triggered event loop
1166 if (!socket)
1170 else
1172
1173 // pendingEncrypt must only be true in between connected and encrypted states
1174 pendingEncrypt = false;
1175 }
1176}
1177
1178#ifndef QT_NO_NETWORKPROXY
1180{
1184 if (h2RequestsToSend.size() > 0)
1185 connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1186 } else { // HTTP
1187 // Need to dequeue the request before we can emit the error.
1188 if (!reply)
1189 connection->d_func()->dequeueRequest(socket);
1190 if (reply)
1191 connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1192 }
1193}
1194#endif
1195
1201
1203 const char *message)
1204{
1205 if (reply)
1206 emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1207 const auto h2RequestsToSendCopy = h2RequestsToSend;
1208 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1209 QHttpNetworkReply *currentReply = httpMessagePair.second;
1210 Q_ASSERT(currentReply);
1211 emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1212 }
1213}
1214
1215#ifndef QT_NO_SSL
1217{
1218 QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
1219 Q_ASSERT(sslSocket);
1220
1222 // ConnectionTypeHTTP2Direct does not rely on ALPN/NPN to negotiate HTTP/2,
1223 // after establishing a secure connection we immediately start sending
1224 // HTTP/2 frames.
1225 switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
1227 QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
1228 if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
1229 // fall through to create a QHttpProtocolHandler
1230 } else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
1231 switchedToHttp2 = true;
1232 protocolHandler.reset(new QHttp2ProtocolHandler(this));
1234 break;
1235 } else {
1237 "detected unknown Next Protocol Negotiation protocol");
1238 break;
1239 }
1240 }
1241 Q_FALLTHROUGH();
1242 case QSslConfiguration::NextProtocolNegotiationUnsupported: // No agreement, try HTTP/1(.1)
1244 protocolHandler.reset(new QHttpProtocolHandler(this));
1245
1246 QSslConfiguration newConfiguration = sslSocket->sslConfiguration();
1247 QList<QByteArray> protocols = newConfiguration.allowedNextProtocols();
1248 const int nProtocols = protocols.size();
1249 // Clear the protocol that we failed to negotiate, so we do not try
1250 // it again on other channels that our connection can create/open.
1252 protocols.removeAll(QSslConfiguration::ALPNProtocolHTTP2);
1253
1254 if (nProtocols > protocols.size()) {
1255 newConfiguration.setAllowedNextProtocols(protocols);
1256 const int channelCount = connection->d_func()->channelCount;
1257 for (int i = 0; i < channelCount; ++i)
1258 connection->d_func()->channels[i].setSslConfiguration(newConfiguration);
1259 }
1260
1262 // We use only one channel for HTTP/2, but normally six for
1263 // HTTP/1.1 - let's restore this number to the reserved number of
1264 // channels:
1265 if (connection->d_func()->activeChannelCount < connection->d_func()->channelCount) {
1266 connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
1267 // re-queue requests from HTTP/2 queue to HTTP queue, if any
1269 }
1270 break;
1271 }
1272 default:
1274 "detected unknown Next Protocol Negotiation protocol");
1275 }
1278 // We have to reset QHttp2ProtocolHandler's state machine, it's a new
1279 // connection and the handler's state is unique per connection.
1280 protocolHandler.reset(new QHttp2ProtocolHandler(this));
1281 }
1282
1283 if (!socket)
1284 return; // ### error
1286 pendingEncrypt = false;
1287
1290 if (!h2RequestsToSend.isEmpty()) {
1291 // Similar to HTTP/1.1 counterpart below:
1292 const auto &pair = std::as_const(h2RequestsToSend).first();
1293 emit pair.second->encrypted();
1294 // In case our peer has sent us its settings (window size, max concurrent streams etc.)
1295 // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
1296 }
1297 } else { // HTTP
1298 if (!reply)
1299 connection->d_func()->dequeueRequest(socket);
1300 if (reply) {
1301 reply->setHttp2WasUsed(false);
1302 Q_ASSERT(reply->d_func()->connectionChannel == this);
1303 emit reply->encrypted();
1304 }
1305 if (reply)
1307 }
1309}
1310
1312{
1313 const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
1314 for (const auto &httpMessagePair : h2RequestsToSendCopy)
1315 connection->d_func()->requeueRequest(httpMessagePair);
1316}
1317
1318void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1319{
1320 if (!socket)
1321 return;
1322 //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1323 // Also pause the connection because socket notifiers may fire while an user
1324 // dialog is displaying
1325 connection->d_func()->pauseConnection();
1326 if (pendingEncrypt && !reply)
1327 connection->d_func()->dequeueRequest(socket);
1329 if (reply)
1330 emit reply->sslErrors(errors);
1331 }
1332#ifndef QT_NO_SSL
1333 else { // HTTP/2
1334 const auto h2RequestsToSendCopy = h2RequestsToSend;
1335 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1336 // emit SSL errors for all replies
1337 QHttpNetworkReply *currentReply = httpMessagePair.second;
1338 Q_ASSERT(currentReply);
1339 emit currentReply->sslErrors(errors);
1340 }
1341 }
1342#endif // QT_NO_SSL
1343 connection->d_func()->resumeConnection();
1344}
1345
1347{
1348 connection->d_func()->pauseConnection();
1349
1350 if (pendingEncrypt && !reply)
1351 connection->d_func()->dequeueRequest(socket);
1352
1354 if (reply)
1356 } else {
1357 const auto h2RequestsToSendCopy = h2RequestsToSend;
1358 for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1359 // emit SSL errors for all replies
1360 QHttpNetworkReply *currentReply = httpMessagePair.second;
1361 Q_ASSERT(currentReply);
1362 emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
1363 }
1364 }
1365
1366 connection->d_func()->resumeConnection();
1367}
1368
1370{
1371 Q_UNUSED(bytes);
1372 // bytes have been written to the socket. write even more of them :)
1373 if (isSocketWriting())
1374 sendRequest();
1375 // otherwise we do nothing
1376}
1377
1378#endif
1379
1381{
1382 // Inlining this function in the header leads to compiler error on
1383 // release-armv5, on at least timebox 9.2 and 10.1.
1384 connection = c;
1385}
1386
1388
1389#include "moc_qhttpnetworkconnectionchannel_p.cpp"
The QAbstractSocket class provides the base functionality common to all socket types.
SocketState
This enum describes the different states in which a socket can be.
static constexpr auto IPv4Protocol
void errorOccurred(QAbstractSocket::SocketError)
void setProxy(const QNetworkProxy &networkProxy)
static constexpr auto AnyIPProtocol
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
SocketError
This enum describes the socket errors that can occur.
static constexpr auto IPv6Protocol
static QAuthenticatorPrivate * getPrivate(QAuthenticator &auth)
The QAuthenticator class provides an authentication object.
\inmodule QtCore
Definition qbytearray.h:57
bool contains(char c) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:660
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
void clear()
Clears the contents of the byte array and makes it null.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Q_INVOKABLE void handleConnectionClosure()
Q_NETWORK_EXPORT bool replaceOrAppend(QAnyStringView name, QAnyStringView newValue)
std::unique_ptr< QAbstractProtocolHandler > protocolHandler
QScopedPointer< QSslConfiguration > sslConfiguration
void _q_connected_local_socket(QLocalSocket *socket)
void setProxy(const QNetworkProxy &networkProxy)
void _q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *)
void setSslConfiguration(const QSslConfiguration &config)
QAbstractSocket::NetworkLayerProtocol networkLayerPreference
void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message)
QPointer< QHttpNetworkConnection > connection
QMultiMap< int, HttpMessagePair > h2RequestsToSend
void _q_sslErrors(const QList< QSslError > &errors)
void setConnection(QHttpNetworkConnection *c)
void _q_connected_abstract_socket(QAbstractSocket *socket)
void _q_error(QAbstractSocket::SocketError)
void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth)
QHttp2Configuration http2Parameters() const
QHttpNetworkConnectionChannel * channels() const
void setConnectionType(ConnectionType type)
QNetworkProxy transparentProxy() const
ConnectionType connectionType() const
void setSslContext(std::shared_ptr< QSslContext > context)
std::shared_ptr< QSslContext > sslContext() const
QHttpNetworkConnection * connection()
QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue=QByteArray()) const override
QString errorString() const
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail=QString())
void setHttp2WasUsed(bool h2Used)
void setReadBufferSize(qint64 size)
void setDownstreamLimited(bool t)
int minorVersion() const override
qint64 contentLength() const override
void setRedirectUrl(const QUrl &url)
void sslErrors(const QList< QSslError > &errors)
int majorVersion() const override
static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy)
QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue=QByteArray()) const override
QUrl url() const override
QNonContiguousByteDevice * uploadByteDevice() const
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
bool isOpen() const
Returns true if the device is open; otherwise returns false.
void bytesWritten(qint64 bytes)
This signal is emitted every time a payload of data has been written to the device's current write ch...
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
QString errorString() const
Returns a human-readable description of the last device error that occurred.
virtual void close()
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
value_type takeFirst()
Definition qlist.h:566
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
The QLocalSocket class provides a local socket.
LocalSocketError
The LocalServerError enumeration represents the errors that can occur.
bool isEmpty() const
Definition qmap.h:940
size_type size() const
Definition qmap.h:938
The QNetworkProxy class provides a network layer proxy.
QNetworkProxy::ProxyType type() const
Returns the proxy type for this instance.
void setHeaders(const QHttpHeaders &newHeaders)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QHttpHeaders headers() const
NetworkError
Indicates all possible error conditions found during the processing of the request.
@ ProxyConnectionRefusedError
@ ProxyAuthenticationRequiredError
@ AuthenticationRequiredError
\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
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
The QSslConfiguration class holds the configuration and state of an SSL connection.
QList< QByteArray > allowedNextProtocols() const
bool isNull() const
Returns true if this is a null QSslConfiguration object.
static const char ALPNProtocolHTTP2[]
static const char NextProtocolHttp1_1[]
The QSslPreSharedKeyAuthenticator class provides authentication data for pre shared keys (PSK) cipher...
static std::shared_ptr< QSslContext > sslContext(QSslSocket *socket)
static void checkSettingSslContext(QSslSocket *, std::shared_ptr< QSslContext >)
The QSslSocket class provides an SSL encrypted socket for both clients and servers.
Definition qsslsocket.h:29
void sslErrors(const QList< QSslError > &errors)
QSslSocket emits this signal after the SSL handshake to indicate that one or more errors have occurre...
void encryptedBytesWritten(qint64 totalBytes)
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
void encrypted()
This signal is emitted when QSslSocket enters encrypted mode.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
The QTcpSocket class provides a TCP socket.
Definition qtcpsocket.h:18
\inmodule QtCore
Definition qurl.h:94
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1882
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
EGLContext ctx
else opt state
[0]
bool is_protocol_upgraded(const QHttpNetworkReply &reply)
void appendProtocolUpgradeHeaders(const QHttp2Configuration &config, QHttpNetworkRequest *request)
auto visit(Fn &&fn, QIODevice *socket, Args &&...args)
QAbstractSocket::SocketState socketState(QIODevice *device)
Combined button and popup list for selecting options.
@ QueuedConnection
@ DirectConnection
#define Q_FALLTHROUGH()
DBusConnection const char DBusError * error
DBusConnection * connection
EGLConfig config
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static const int reconnectAttemptsDefault
QPair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair
#define qWarning
Definition qlogging.h:166
static const QMetaObjectPrivate * priv(const uint *data)
GLenum type
GLuint GLsizei const GLchar * message
GLint first
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
unsigned short quint16
Definition qtypes.h:48
long long qint64
Definition qtypes.h:60
QObject::connect nullptr
QTcpSocket * socket
[1]
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...