Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qhttpthreaddelegate.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// Qt-Security score:significant reason:default
4
5//#define QHTTPTHREADDELEGATE_DEBUG
7
8#include <QThread>
9#include <QTimer>
10#include <QAuthenticator>
11#include <QEventLoop>
12#include <QCryptographicHash>
13#include <QtCore/qscopedvaluerollback.h>
14
15#include "private/qhttpnetworkreply_p.h"
16#include "private/qnetworkaccesscache_p.h"
17#include "private/qnoncontiguousbytedevice_p.h"
18
20
21using namespace Qt::StringLiterals;
22
23static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
24{
25 QNetworkReply::NetworkError code;
26 // we've got an error
27 switch (httpStatusCode) {
28 case 400: // Bad Request
29 code = QNetworkReply::ProtocolInvalidOperationError;
30 break;
31
32 case 401: // Authorization required
33 code = QNetworkReply::AuthenticationRequiredError;
34 break;
35
36 case 403: // Access denied
37 code = QNetworkReply::ContentAccessDenied;
38 break;
39
40 case 404: // Not Found
41 code = QNetworkReply::ContentNotFoundError;
42 break;
43
44 case 405: // Method Not Allowed
45 code = QNetworkReply::ContentOperationNotPermittedError;
46 break;
47
48 case 407:
49 code = QNetworkReply::ProxyAuthenticationRequiredError;
50 break;
51
52 case 409: // Resource Conflict
53 code = QNetworkReply::ContentConflictError;
54 break;
55
56 case 410: // Content no longer available
57 code = QNetworkReply::ContentGoneError;
58 break;
59
60 case 418: // I'm a teapot
61 code = QNetworkReply::ProtocolInvalidOperationError;
62 break;
63
64 case 500: // Internal Server Error
65 code = QNetworkReply::InternalServerError;
66 break;
67
68 case 501: // Server does not support this functionality
69 code = QNetworkReply::OperationNotImplementedError;
70 break;
71
72 case 503: // Service unavailable
73 code = QNetworkReply::ServiceUnavailableError;
74 break;
75
76 default:
77 if (httpStatusCode > 500) {
78 // some kind of server error
79 code = QNetworkReply::UnknownServerError;
80 } else if (httpStatusCode >= 400) {
81 // content error we did not handle above
82 code = QNetworkReply::UnknownContentError;
83 } else {
84 qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
85 httpStatusCode, qPrintable(url.toString()));
86 code = QNetworkReply::ProtocolFailure;
87 }
88 }
89
90 return code;
91}
92
93
94static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &peerVerifyName)
95{
96 QString result;
97 QUrl copy = url;
98 QString scheme = copy.scheme();
99 bool isEncrypted = scheme == "https"_L1 || scheme == "preconnect-https"_L1;
100 const bool isLocalSocket = scheme.startsWith("unix"_L1);
101 if (!isLocalSocket)
102 copy.setPort(copy.port(isEncrypted ? 443 : 80));
103 if (scheme == "preconnect-http"_L1)
104 copy.setScheme("http"_L1);
105 else if (scheme == "preconnect-https"_L1)
106 copy.setScheme("https"_L1);
107 result = copy.toString(QUrl::RemoveUserInfo | QUrl::RemovePath |
108 QUrl::RemoveQuery | QUrl::RemoveFragment | QUrl::FullyEncoded);
109
110#ifndef QT_NO_NETWORKPROXY
111 if (proxy && proxy->type() != QNetworkProxy::NoProxy) {
112 QUrl key;
113
114 switch (proxy->type()) {
115 case QNetworkProxy::Socks5Proxy:
116 key.setScheme("proxy-socks5"_L1);
117 break;
118
119 case QNetworkProxy::HttpProxy:
120 case QNetworkProxy::HttpCachingProxy:
121 key.setScheme("proxy-http"_L1);
122 break;
123
124 default:
125 break;
126 }
127
128 if (!key.scheme().isEmpty()) {
129 const QByteArray obfuscatedPassword = QCryptographicHash::hash(proxy->password().toUtf8(),
130 QCryptographicHash::Sha1).toHex();
131 key.setUserName(proxy->user());
132 key.setPassword(QString::fromUtf8(obfuscatedPassword));
133 key.setHost(proxy->hostName());
134 key.setPort(proxy->port());
135 key.setQuery(result);
136 result = key.toString(QUrl::FullyEncoded);
137 }
138 }
139#else
140 Q_UNUSED(proxy);
141#endif
142 if (!peerVerifyName.isEmpty())
143 result += u':' + peerVerifyName;
144 return "http-connection:" + std::move(result).toLatin1();
145}
146
149{
150 // Q_OBJECT
151public:
152 QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, bool isLocalSocket,
153 QHttpNetworkConnection::ConnectionType connectionType)
156 {
157
158 }
159
160 virtual void dispose() override
161 {
162#if 0 // sample code; do this right with the API
163 Q_ASSERT(!isWorking());
164#endif
165 delete this;
166 }
167};
168
169
171
172
174{
175 // It could be that the main thread has asked us to shut down, so we need to delete the HTTP reply
176 if (httpReply) {
177 delete httpReply;
178 }
179
180 // Get the object cache that stores our QHttpNetworkConnection objects
181 // and release the entry for this QHttpNetworkConnection
182 if (connections.hasLocalData() && !cacheKey.isEmpty()) {
183 connections.localData()->releaseEntry(cacheKey);
184 }
185}
186
187
188QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
189 QObject(parent)
190 , ssl(false)
191 , downloadBufferMaximumSize(0)
192 , readBufferMaxSize(0)
193 , bytesEmitted(0)
194 , pendingDownloadData()
195 , pendingDownloadProgress()
196 , synchronous(false)
197 , connectionCacheExpiryTimeoutSeconds(-1)
199 , isPipeliningUsed(false)
200 , isHttp2Used(false)
201 , incomingContentLength(-1)
202 , removedContentLength(-1)
203 , incomingErrorCode(QNetworkReply::NoError)
204 , downloadBuffer()
205 , httpConnection(nullptr)
206 , httpReply(nullptr)
207 , synchronousRequestLoop(nullptr)
208{
209}
210
211// This is invoked as BlockingQueuedConnection from QNetworkAccessHttpBackend in the user thread
213{
214#ifdef QHTTPTHREADDELEGATE_DEBUG
215 qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId();
216#endif
217 synchronous = true;
218
219 QEventLoop synchronousRequestLoop;
220 QScopedValueRollback<QEventLoop*> guard(this->synchronousRequestLoop, &synchronousRequestLoop);
221
222 // Worst case timeout
223 QTimer::singleShot(30*1000, this, SLOT(abortRequest()));
224
225 QMetaObject::invokeMethod(this, "startRequest", Qt::QueuedConnection);
226 synchronousRequestLoop.exec();
227
228 connections.localData()->releaseEntry(cacheKey);
229 connections.setLocalData(nullptr);
230
231#ifdef QHTTPTHREADDELEGATE_DEBUG
232 qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId() << "finished";
233#endif
234}
235
236
237// This is invoked as QueuedConnection from QNetworkAccessHttpBackend in the user thread
238void QHttpThreadDelegate::startRequest()
239{
240#ifdef QHTTPTHREADDELEGATE_DEBUG
241 qDebug() << "QHttpThreadDelegate::startRequest() thread=" << QThread::currentThreadId();
242#endif
243 // Check QThreadStorage for the QNetworkAccessCache
244 // If not there, create this connection cache
245 if (!connections.hasLocalData()) {
246 connections.setLocalData(new QNetworkAccessCache());
247 }
248
249 // check if we have an open connection to this host
250 QUrl urlCopy = httpRequest.url();
251 const bool isLocalSocket = urlCopy.scheme().startsWith("unix"_L1);
252 if (!isLocalSocket)
253 urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
254
255 QHttpNetworkConnection::ConnectionType connectionType
256 = httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
257 : QHttpNetworkConnection::ConnectionTypeHTTP;
258 if (httpRequest.isHTTP2Direct()) {
259 Q_ASSERT(!httpRequest.isHTTP2Allowed());
260 connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2Direct;
261 }
262
263 // Use HTTP/1.1 if h2c is not allowed and we would otherwise choose to use it
264 if (!ssl && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
265 && !httpRequest.isH2cAllowed()) {
266 connectionType = QHttpNetworkConnection::ConnectionTypeHTTP;
267 }
268
269#if QT_CONFIG(ssl)
270 // See qnetworkreplyhttpimpl, delegate's initialization code.
271 Q_ASSERT(!ssl || incomingSslConfiguration);
272#endif // QT_CONFIG(ssl)
273
274 const bool isH2 = httpRequest.isHTTP2Allowed() || httpRequest.isHTTP2Direct();
275 if (isH2) {
276#if QT_CONFIG(ssl)
277 if (ssl) {
278 if (!httpRequest.isHTTP2Direct()) {
279 QList<QByteArray> protocols;
280 protocols << QSslConfiguration::ALPNProtocolHTTP2
281 << QSslConfiguration::NextProtocolHttp1_1;
282 incomingSslConfiguration->setAllowedNextProtocols(protocols);
283 }
284 urlCopy.setScheme(QStringLiteral("h2s"));
285 } else
286#endif // QT_CONFIG(ssl)
287 {
288 if (isLocalSocket)
289 urlCopy.setScheme(QStringLiteral("unix+h2"));
290 else
291 urlCopy.setScheme(QStringLiteral("h2"));
292 }
293 }
294
295 QString extraData = httpRequest.peerVerifyName();
296 if (isLocalSocket) {
297 if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
298 extraData = path;
299 }
300
301#ifndef QT_NO_NETWORKPROXY
302 if (transparentProxy.type() != QNetworkProxy::NoProxy)
303 cacheKey = makeCacheKey(urlCopy, &transparentProxy, httpRequest.peerVerifyName());
304 else if (cacheProxy.type() != QNetworkProxy::NoProxy)
305 cacheKey = makeCacheKey(urlCopy, &cacheProxy, httpRequest.peerVerifyName());
306 else
307#endif
308 cacheKey = makeCacheKey(urlCopy, nullptr, httpRequest.peerVerifyName());
309
310 // the http object is actually a QHttpNetworkConnection
311 httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
312 if (!httpConnection) {
313
314 QString host = urlCopy.host();
315 // Update the host if a unix socket path or named pipe is used:
316 if (isLocalSocket) {
317 if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
318 host = path;
319 }
320
321 // no entry in cache; create an object
322 // the http object is actually a QHttpNetworkConnection
323 httpConnection = new QNetworkAccessCachedHttpConnection(
324 http1Parameters.numberOfConnectionsPerHost(), host, urlCopy.port(), ssl,
325 isLocalSocket, connectionType);
326 if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
327 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
328 httpConnection->setHttp2Parameters(http2Parameters);
329 }
330
331 httpConnection->setTcpKeepAliveParameters(tcpKeepAliveParameters);
332#ifndef QT_NO_SSL
333 // Set the QSslConfiguration from this QNetworkRequest.
334 if (ssl)
335 httpConnection->setSslConfiguration(*incomingSslConfiguration);
336#endif
337
338#ifndef QT_NO_NETWORKPROXY
339 httpConnection->setTransparentProxy(transparentProxy);
340 httpConnection->setCacheProxy(cacheProxy);
341#endif
342 httpConnection->setPeerVerifyName(httpRequest.peerVerifyName());
343 // cache the QHttpNetworkConnection corresponding to this cache key
344 connections.localData()->addEntry(cacheKey, httpConnection, connectionCacheExpiryTimeoutSeconds);
345 } else {
346 if (httpRequest.withCredentials()) {
347 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), nullptr);
348 if (!credential.user.isEmpty() && !credential.password.isEmpty()) {
349 QAuthenticator auth;
350 auth.setUser(credential.user);
351 auth.setPassword(credential.password);
352 httpConnection->d_func()->copyCredentials(-1, &auth, false);
353 }
354 }
355 }
356
357 // Send the request to the connection
358 httpReply = httpConnection->sendRequest(httpRequest);
359 httpReply->setParent(this);
360
361 // Connect the reply signals that we need to handle and then forward
362 if (synchronous) {
363 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));
364 connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));
365 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
366 this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
367
368 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
369 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
370#ifndef QT_NO_NETWORKPROXY
371 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
372 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
373#endif
374
375 // Don't care about ignored SSL errors for now in the synchronous HTTP case.
376 } else if (!synchronous) {
377 connect(httpReply,SIGNAL(socketStartedConnecting()), this, SIGNAL(socketStartedConnecting()));
378 connect(httpReply,SIGNAL(requestSent()), this, SIGNAL(requestSent()));
379 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
380 connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
381 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
382 this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
383 // some signals are only interesting when normal asynchronous style is used
384 connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
385 connect(httpReply,SIGNAL(dataReadProgress(qint64,qint64)), this, SLOT(dataReadProgressSlot(qint64,qint64)));
386#ifndef QT_NO_SSL
387 connect(httpReply,SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
388 connect(httpReply,SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
389 connect(httpReply,SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
390 this, SLOT(preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator*)));
391#endif
392
393 // In the asynchronous HTTP case we can just forward those signals
394 // Connect the reply signals that we can directly forward
395 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
396 this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
397#ifndef QT_NO_NETWORKPROXY
398 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
399 this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
400#endif
401 }
402
403 connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
404 this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
405 if (httpReply->errorCode() != QNetworkReply::NoError) {
406 if (synchronous)
407 synchronousFinishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
408 else
409 finishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
410 }
411}
412
413// This gets called from the user thread or by the synchronous HTTP timeout timer
415{
416#ifdef QHTTPTHREADDELEGATE_DEBUG
417 qDebug() << "QHttpThreadDelegate::abortRequest() thread=" << QThread::currentThreadId() << "sync=" << synchronous;
418#endif
419 if (httpReply) {
420 httpReply->abort();
421 delete httpReply;
422 httpReply = nullptr;
423 }
424
425 // Got aborted by the timeout timer
426 if (synchronous) {
427 incomingErrorCode = QNetworkReply::TimeoutError;
428 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
429 } else {
430 //only delete this for asynchronous mode or QNetworkAccessHttpBackend will crash - see QNetworkAccessHttpBackend::postRequest()
431 this->deleteLater();
432 }
433}
434
436{
437#ifdef QHTTPTHREADDELEGATE_DEBUG
438 qDebug() << "QHttpThreadDelegate::readBufferSizeChanged() size " << size;
439#endif
440 if (httpReply) {
441 httpReply->setDownstreamLimited(size > 0);
442 httpReply->setReadBufferSize(size);
443 readBufferMaxSize = size;
444 }
445}
446
448{
449 if (readBufferMaxSize) {
450 bytesEmitted -= size;
451
452 QMetaObject::invokeMethod(this, "readyReadSlot", Qt::QueuedConnection);
453 }
454}
455
456void QHttpThreadDelegate::readyReadSlot()
457{
458 if (!httpReply)
459 return;
460
461 // Don't do in zerocopy case
462 if (!downloadBuffer.isNull())
463 return;
464
465 if (readBufferMaxSize) {
466 if (bytesEmitted < readBufferMaxSize) {
467 qint64 sizeEmitted = 0;
468 while (httpReply->readAnyAvailable() && (sizeEmitted < (readBufferMaxSize-bytesEmitted))) {
469 if (httpReply->sizeNextBlock() > (readBufferMaxSize-bytesEmitted)) {
470 sizeEmitted = readBufferMaxSize-bytesEmitted;
471 bytesEmitted += sizeEmitted;
472 pendingDownloadData->fetchAndAddRelease(1);
473 emit downloadData(httpReply->read(sizeEmitted));
474 } else {
475 sizeEmitted = httpReply->sizeNextBlock();
476 bytesEmitted += sizeEmitted;
477 pendingDownloadData->fetchAndAddRelease(1);
478 emit downloadData(httpReply->readAny());
479 }
480 }
481 } else {
482 // We need to wait until we empty data from the read buffer in the reply.
483 }
484
485 } else {
486 while (httpReply->readAnyAvailable()) {
487 pendingDownloadData->fetchAndAddRelease(1);
488 emit downloadData(httpReply->readAny());
489 }
490 }
491}
492
493static QString makeServerErrorString(int code, const QUrl &url, const QString &reasonPhrase)
494{
495 QString msg;
496 if (!reasonPhrase.isEmpty()) {
497 msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
498 "Error transferring %1 - server replied: %2"))
499 .arg(url.toString(), reasonPhrase);
500 } else {
501 msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
502 "Error transferring %1 - server replied with status code %2"))
503 .arg(url.toString(), QString::number(code));
504 }
505 return msg;
506}
507
509{
510 if (!httpReply)
511 return;
512
513#ifdef QHTTPTHREADDELEGATE_DEBUG
514 qDebug() << "QHttpThreadDelegate::finishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
515#endif
516
517 // If there is still some data left emit that now
518 while (httpReply->readAnyAvailable()) {
519 pendingDownloadData->fetchAndAddRelease(1);
520 emit downloadData(httpReply->readAny());
521 }
522
523#ifndef QT_NO_SSL
524 if (ssl)
525 emit sslConfigurationChanged(httpReply->sslConfiguration());
526#endif
527
528 if (httpReply->statusCode() >= 400) {
529 // it's an error reply
530 QString msg = makeServerErrorString(httpReply->statusCode(), httpRequest.url(),
531 httpReply->reasonPhrase());
532 emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
533 }
534
535 if (httpRequest.isFollowRedirects() && httpReply->isRedirecting())
536 emit redirected(httpReply->redirectUrl(), httpReply->statusCode(), httpReply->request().redirectCount() - 1);
537
538 emit downloadFinished();
539
540 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
541 QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
542 httpReply = nullptr;
543}
544
546{
547 if (!httpReply)
548 return;
549
550#ifdef QHTTPTHREADDELEGATE_DEBUG
551 qDebug() << "QHttpThreadDelegate::synchronousFinishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
552#endif
553 if (httpReply->statusCode() >= 400) {
554 // it's an error reply
555 incomingErrorDetail = makeServerErrorString(httpReply->statusCode(), httpRequest.url(),
556 httpReply->reasonPhrase());
557 incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
558 }
559
560 isCompressed = httpReply->isCompressed();
561 synchronousDownloadData = httpReply->readAll();
562
563 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
564 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
565 httpReply = nullptr;
566}
567
568void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
569{
570 if (!httpReply)
571 return;
572
573#ifdef QHTTPTHREADDELEGATE_DEBUG
574 qDebug() << "QHttpThreadDelegate::finishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
575#endif
576
577#ifndef QT_NO_SSL
578 if (ssl)
579 emit sslConfigurationChanged(httpReply->sslConfiguration());
580#endif
581 emit error(errorCode,detail);
582 emit downloadFinished();
583
584
585 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
586 QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
587 httpReply = nullptr;
588}
589
590
591void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
592{
593 if (!httpReply)
594 return;
595
596#ifdef QHTTPTHREADDELEGATE_DEBUG
597 qDebug() << "QHttpThreadDelegate::synchronousFinishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
598#endif
599 incomingErrorCode = errorCode;
600 incomingErrorDetail = detail;
601
602 synchronousDownloadData = httpReply->readAll();
603
604 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
605 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
606 httpReply = nullptr;
607}
608
610{
611 if (!httpReply)
612 return;
613
614#ifdef QHTTPTHREADDELEGATE_DEBUG
615 qDebug() << "QHttpThreadDelegate::headerChangedSlot() thread=" << QThread::currentThreadId();
616#endif
617
618#ifndef QT_NO_SSL
619 if (ssl)
620 emit sslConfigurationChanged(httpReply->sslConfiguration());
621#endif
622
623 // Is using a zerocopy buffer allowed by user and possible with this reply?
624 if (httpReply->supportsUserProvidedDownloadBuffer()
625 && (downloadBufferMaximumSize > 0) && (httpReply->contentLength() <= downloadBufferMaximumSize)) {
626 char *buf = new (std::nothrow) char[httpReply->contentLength()];
627 // in out of memory situations, don't use downloadBuffer.
628 if (buf) {
629 downloadBuffer = QSharedPointer<char>(buf, [](auto p) { delete[] p; });
630 httpReply->setUserProvidedDownloadBuffer(buf);
631 }
632 }
633
634 // We fetch this into our own
635 incomingHeaders = httpReply->header();
636 incomingStatusCode = httpReply->statusCode();
637 incomingReasonPhrase = httpReply->reasonPhrase();
638 isPipeliningUsed = httpReply->isPipeliningUsed();
639 incomingContentLength = httpReply->contentLength();
640 removedContentLength = httpReply->removedContentLength();
641 isHttp2Used = httpReply->isHttp2Used();
642 isCompressed = httpReply->isCompressed();
643
644 emit downloadMetaData(incomingHeaders,
645 incomingStatusCode,
646 incomingReasonPhrase,
647 isPipeliningUsed,
648 downloadBuffer,
649 incomingContentLength,
650 removedContentLength,
651 isHttp2Used,
652 isCompressed);
653}
654
656{
657 if (!httpReply)
658 return;
659
660#ifdef QHTTPTHREADDELEGATE_DEBUG
661 qDebug() << "QHttpThreadDelegate::synchronousHeaderChangedSlot() thread=" << QThread::currentThreadId();
662#endif
663 // Store the information we need in this object, the QNetworkAccessHttpBackend will later read it
664 incomingHeaders = httpReply->header();
665 incomingStatusCode = httpReply->statusCode();
666 incomingReasonPhrase = httpReply->reasonPhrase();
667 isPipeliningUsed = httpReply->isPipeliningUsed();
668 isHttp2Used = httpReply->isHttp2Used();
669 incomingContentLength = httpReply->contentLength();
670}
671
672
673void QHttpThreadDelegate::dataReadProgressSlot(qint64 done, qint64 total)
674{
675 // If we don't have a download buffer don't attempt to go this codepath
676 // It is not used by QNetworkAccessHttpBackend
677 if (downloadBuffer.isNull())
678 return;
679
680 pendingDownloadProgress->fetchAndAddRelease(1);
681 emit downloadProgress(done, total);
682}
683
684void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
685{
686 authenticationManager->cacheCredentials(request.url(), authenticator);
687}
688
689
690#ifndef QT_NO_SSL
692{
693 if (!httpReply)
694 return;
695
696 emit sslConfigurationChanged(httpReply->sslConfiguration());
697 emit encrypted();
698}
699
700void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
701{
702 if (!httpReply)
703 return;
704
705 emit sslConfigurationChanged(httpReply->sslConfiguration());
706
707 bool ignoreAll = false;
708 QList<QSslError> specificErrors;
709 emit sslErrors(errors, &ignoreAll, &specificErrors);
710 if (ignoreAll)
711 httpReply->ignoreSslErrors();
712 if (!specificErrors.isEmpty())
713 httpReply->ignoreSslErrors(specificErrors);
714}
715
716void QHttpThreadDelegate::preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator)
717{
718 if (!httpReply)
719 return;
720
721 emit preSharedKeyAuthenticationRequired(authenticator);
722}
723#endif
724
725void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)
726{
727 if (!httpReply)
728 return;
729
730 Q_UNUSED(request);
731#ifdef QHTTPTHREADDELEGATE_DEBUG
732 qDebug() << "QHttpThreadDelegate::synchronousAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
733#endif
734
735 // Ask the credential cache
736 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a);
737 if (!credential.isNull()) {
738 a->setUser(credential.user);
739 a->setPassword(credential.password);
740 }
741
742 // Disconnect this connection now since we only want to ask the authentication cache once.
743 QObject::disconnect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
744 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
745}
746
747#ifndef QT_NO_NETWORKPROXY
748void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a)
749{
750 if (!httpReply)
751 return;
752
753#ifdef QHTTPTHREADDELEGATE_DEBUG
754 qDebug() << "QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
755#endif
756 // Ask the credential cache
757 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a);
758 if (!credential.isNull()) {
759 a->setUser(credential.user);
760 a->setPassword(credential.password);
761 }
762
763#ifndef QT_NO_NETWORKPROXY
764 // Disconnect this connection now since we only want to ask the authentication cache once.
765 QObject::disconnect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
766 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
767#endif
768}
769
770#endif
771
772QT_END_NAMESPACE
773
774#include "moc_qhttpthreaddelegate_p.cpp"
void cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
QHttpNetworkReply * httpReply
void readBufferSizeChanged(qint64 size)
void synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *)
void synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail=QString())
void socketStartedConnecting()
void sslErrorsSlot(const QList< QSslError > &errors)
QNetworkAccessCachedHttpConnection * httpConnection
void preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator)
void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)
void synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &, QAuthenticator *)
void finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail=QString())
void readBufferFreed(qint64 size)
static QThreadStorage< QNetworkAccessCache * > connections
void dataReadProgressSlot(qint64 done, qint64 total)
QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, bool isLocalSocket, QHttpNetworkConnection::ConnectionType connectionType)
Combined button and popup list for selecting options.
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &peerVerifyName)
static QString makeServerErrorString(int code, const QUrl &url, const QString &reasonPhrase)