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#ifndef QT_NO_SSL
331 // Set the QSslConfiguration from this QNetworkRequest.
332 if (ssl)
333 httpConnection->setSslConfiguration(*incomingSslConfiguration);
334#endif
335
336#ifndef QT_NO_NETWORKPROXY
337 httpConnection->setTransparentProxy(transparentProxy);
338 httpConnection->setCacheProxy(cacheProxy);
339#endif
340 httpConnection->setPeerVerifyName(httpRequest.peerVerifyName());
341 // cache the QHttpNetworkConnection corresponding to this cache key
342 connections.localData()->addEntry(cacheKey, httpConnection, connectionCacheExpiryTimeoutSeconds);
343 } else {
344 if (httpRequest.withCredentials()) {
345 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), nullptr);
346 if (!credential.user.isEmpty() && !credential.password.isEmpty()) {
347 QAuthenticator auth;
348 auth.setUser(credential.user);
349 auth.setPassword(credential.password);
350 httpConnection->d_func()->copyCredentials(-1, &auth, false);
351 }
352 }
353 }
354
355 // Send the request to the connection
356 httpReply = httpConnection->sendRequest(httpRequest);
357 httpReply->setParent(this);
358
359 // Connect the reply signals that we need to handle and then forward
360 if (synchronous) {
361 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));
362 connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));
363 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
364 this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
365
366 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
367 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
368#ifndef QT_NO_NETWORKPROXY
369 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
370 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
371#endif
372
373 // Don't care about ignored SSL errors for now in the synchronous HTTP case.
374 } else if (!synchronous) {
375 connect(httpReply,SIGNAL(socketStartedConnecting()), this, SIGNAL(socketStartedConnecting()));
376 connect(httpReply,SIGNAL(requestSent()), this, SIGNAL(requestSent()));
377 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
378 connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
379 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
380 this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
381 // some signals are only interesting when normal asynchronous style is used
382 connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
383 connect(httpReply,SIGNAL(dataReadProgress(qint64,qint64)), this, SLOT(dataReadProgressSlot(qint64,qint64)));
384#ifndef QT_NO_SSL
385 connect(httpReply,SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
386 connect(httpReply,SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
387 connect(httpReply,SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
388 this, SLOT(preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator*)));
389#endif
390
391 // In the asynchronous HTTP case we can just forward those signals
392 // Connect the reply signals that we can directly forward
393 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
394 this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
395#ifndef QT_NO_NETWORKPROXY
396 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
397 this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
398#endif
399 }
400
401 connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
402 this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
403 if (httpReply->errorCode() != QNetworkReply::NoError) {
404 if (synchronous)
405 synchronousFinishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
406 else
407 finishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
408 }
409}
410
411// This gets called from the user thread or by the synchronous HTTP timeout timer
413{
414#ifdef QHTTPTHREADDELEGATE_DEBUG
415 qDebug() << "QHttpThreadDelegate::abortRequest() thread=" << QThread::currentThreadId() << "sync=" << synchronous;
416#endif
417 if (httpReply) {
418 httpReply->abort();
419 delete httpReply;
420 httpReply = nullptr;
421 }
422
423 // Got aborted by the timeout timer
424 if (synchronous) {
425 incomingErrorCode = QNetworkReply::TimeoutError;
426 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
427 } else {
428 //only delete this for asynchronous mode or QNetworkAccessHttpBackend will crash - see QNetworkAccessHttpBackend::postRequest()
429 this->deleteLater();
430 }
431}
432
434{
435#ifdef QHTTPTHREADDELEGATE_DEBUG
436 qDebug() << "QHttpThreadDelegate::readBufferSizeChanged() size " << size;
437#endif
438 if (httpReply) {
439 httpReply->setDownstreamLimited(size > 0);
440 httpReply->setReadBufferSize(size);
441 readBufferMaxSize = size;
442 }
443}
444
446{
447 if (readBufferMaxSize) {
448 bytesEmitted -= size;
449
450 QMetaObject::invokeMethod(this, "readyReadSlot", Qt::QueuedConnection);
451 }
452}
453
454void QHttpThreadDelegate::readyReadSlot()
455{
456 if (!httpReply)
457 return;
458
459 // Don't do in zerocopy case
460 if (!downloadBuffer.isNull())
461 return;
462
463 if (readBufferMaxSize) {
464 if (bytesEmitted < readBufferMaxSize) {
465 qint64 sizeEmitted = 0;
466 while (httpReply->readAnyAvailable() && (sizeEmitted < (readBufferMaxSize-bytesEmitted))) {
467 if (httpReply->sizeNextBlock() > (readBufferMaxSize-bytesEmitted)) {
468 sizeEmitted = readBufferMaxSize-bytesEmitted;
469 bytesEmitted += sizeEmitted;
470 pendingDownloadData->fetchAndAddRelease(1);
471 emit downloadData(httpReply->read(sizeEmitted));
472 } else {
473 sizeEmitted = httpReply->sizeNextBlock();
474 bytesEmitted += sizeEmitted;
475 pendingDownloadData->fetchAndAddRelease(1);
476 emit downloadData(httpReply->readAny());
477 }
478 }
479 } else {
480 // We need to wait until we empty data from the read buffer in the reply.
481 }
482
483 } else {
484 while (httpReply->readAnyAvailable()) {
485 pendingDownloadData->fetchAndAddRelease(1);
486 emit downloadData(httpReply->readAny());
487 }
488 }
489}
490
491static QString makeServerErrorString(int code, const QUrl &url, const QString &reasonPhrase)
492{
493 QString msg;
494 if (!reasonPhrase.isEmpty()) {
495 msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
496 "Error transferring %1 - server replied: %2"))
497 .arg(url.toString(), reasonPhrase);
498 } else {
499 msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
500 "Error transferring %1 - server replied with status code %2"))
501 .arg(url.toString(), QString::number(code));
502 }
503 return msg;
504}
505
507{
508 if (!httpReply)
509 return;
510
511#ifdef QHTTPTHREADDELEGATE_DEBUG
512 qDebug() << "QHttpThreadDelegate::finishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
513#endif
514
515 // If there is still some data left emit that now
516 while (httpReply->readAnyAvailable()) {
517 pendingDownloadData->fetchAndAddRelease(1);
518 emit downloadData(httpReply->readAny());
519 }
520
521#ifndef QT_NO_SSL
522 if (ssl)
523 emit sslConfigurationChanged(httpReply->sslConfiguration());
524#endif
525
526 if (httpReply->statusCode() >= 400) {
527 // it's an error reply
528 QString msg = makeServerErrorString(httpReply->statusCode(), httpRequest.url(),
529 httpReply->reasonPhrase());
530 emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
531 }
532
533 if (httpRequest.isFollowRedirects() && httpReply->isRedirecting())
534 emit redirected(httpReply->redirectUrl(), httpReply->statusCode(), httpReply->request().redirectCount() - 1);
535
536 emit downloadFinished();
537
538 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
539 QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
540 httpReply = nullptr;
541}
542
544{
545 if (!httpReply)
546 return;
547
548#ifdef QHTTPTHREADDELEGATE_DEBUG
549 qDebug() << "QHttpThreadDelegate::synchronousFinishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
550#endif
551 if (httpReply->statusCode() >= 400) {
552 // it's an error reply
553 incomingErrorDetail = makeServerErrorString(httpReply->statusCode(), httpRequest.url(),
554 httpReply->reasonPhrase());
555 incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
556 }
557
558 isCompressed = httpReply->isCompressed();
559 synchronousDownloadData = httpReply->readAll();
560
561 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
562 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
563 httpReply = nullptr;
564}
565
566void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
567{
568 if (!httpReply)
569 return;
570
571#ifdef QHTTPTHREADDELEGATE_DEBUG
572 qDebug() << "QHttpThreadDelegate::finishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
573#endif
574
575#ifndef QT_NO_SSL
576 if (ssl)
577 emit sslConfigurationChanged(httpReply->sslConfiguration());
578#endif
579 emit error(errorCode,detail);
580 emit downloadFinished();
581
582
583 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
584 QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
585 httpReply = nullptr;
586}
587
588
589void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
590{
591 if (!httpReply)
592 return;
593
594#ifdef QHTTPTHREADDELEGATE_DEBUG
595 qDebug() << "QHttpThreadDelegate::synchronousFinishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
596#endif
597 incomingErrorCode = errorCode;
598 incomingErrorDetail = detail;
599
600 synchronousDownloadData = httpReply->readAll();
601
602 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
603 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
604 httpReply = nullptr;
605}
606
608{
609 if (!httpReply)
610 return;
611
612#ifdef QHTTPTHREADDELEGATE_DEBUG
613 qDebug() << "QHttpThreadDelegate::headerChangedSlot() thread=" << QThread::currentThreadId();
614#endif
615
616#ifndef QT_NO_SSL
617 if (ssl)
618 emit sslConfigurationChanged(httpReply->sslConfiguration());
619#endif
620
621 // Is using a zerocopy buffer allowed by user and possible with this reply?
622 if (httpReply->supportsUserProvidedDownloadBuffer()
623 && (downloadBufferMaximumSize > 0) && (httpReply->contentLength() <= downloadBufferMaximumSize)) {
624 char *buf = new (std::nothrow) char[httpReply->contentLength()];
625 // in out of memory situations, don't use downloadBuffer.
626 if (buf) {
627 downloadBuffer = QSharedPointer<char>(buf, [](auto p) { delete[] p; });
628 httpReply->setUserProvidedDownloadBuffer(buf);
629 }
630 }
631
632 // We fetch this into our own
633 incomingHeaders = httpReply->header();
634 incomingStatusCode = httpReply->statusCode();
635 incomingReasonPhrase = httpReply->reasonPhrase();
636 isPipeliningUsed = httpReply->isPipeliningUsed();
637 incomingContentLength = httpReply->contentLength();
638 removedContentLength = httpReply->removedContentLength();
639 isHttp2Used = httpReply->isHttp2Used();
640 isCompressed = httpReply->isCompressed();
641
642 emit downloadMetaData(incomingHeaders,
643 incomingStatusCode,
644 incomingReasonPhrase,
645 isPipeliningUsed,
646 downloadBuffer,
647 incomingContentLength,
648 removedContentLength,
649 isHttp2Used,
650 isCompressed);
651}
652
654{
655 if (!httpReply)
656 return;
657
658#ifdef QHTTPTHREADDELEGATE_DEBUG
659 qDebug() << "QHttpThreadDelegate::synchronousHeaderChangedSlot() thread=" << QThread::currentThreadId();
660#endif
661 // Store the information we need in this object, the QNetworkAccessHttpBackend will later read it
662 incomingHeaders = httpReply->header();
663 incomingStatusCode = httpReply->statusCode();
664 incomingReasonPhrase = httpReply->reasonPhrase();
665 isPipeliningUsed = httpReply->isPipeliningUsed();
666 isHttp2Used = httpReply->isHttp2Used();
667 incomingContentLength = httpReply->contentLength();
668}
669
670
671void QHttpThreadDelegate::dataReadProgressSlot(qint64 done, qint64 total)
672{
673 // If we don't have a download buffer don't attempt to go this codepath
674 // It is not used by QNetworkAccessHttpBackend
675 if (downloadBuffer.isNull())
676 return;
677
678 pendingDownloadProgress->fetchAndAddRelease(1);
679 emit downloadProgress(done, total);
680}
681
682void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
683{
684 authenticationManager->cacheCredentials(request.url(), authenticator);
685}
686
687
688#ifndef QT_NO_SSL
690{
691 if (!httpReply)
692 return;
693
694 emit sslConfigurationChanged(httpReply->sslConfiguration());
695 emit encrypted();
696}
697
698void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
699{
700 if (!httpReply)
701 return;
702
703 emit sslConfigurationChanged(httpReply->sslConfiguration());
704
705 bool ignoreAll = false;
706 QList<QSslError> specificErrors;
707 emit sslErrors(errors, &ignoreAll, &specificErrors);
708 if (ignoreAll)
709 httpReply->ignoreSslErrors();
710 if (!specificErrors.isEmpty())
711 httpReply->ignoreSslErrors(specificErrors);
712}
713
714void QHttpThreadDelegate::preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator)
715{
716 if (!httpReply)
717 return;
718
719 emit preSharedKeyAuthenticationRequired(authenticator);
720}
721#endif
722
723void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)
724{
725 if (!httpReply)
726 return;
727
728 Q_UNUSED(request);
729#ifdef QHTTPTHREADDELEGATE_DEBUG
730 qDebug() << "QHttpThreadDelegate::synchronousAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
731#endif
732
733 // Ask the credential cache
734 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a);
735 if (!credential.isNull()) {
736 a->setUser(credential.user);
737 a->setPassword(credential.password);
738 }
739
740 // Disconnect this connection now since we only want to ask the authentication cache once.
741 QObject::disconnect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
742 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
743}
744
745#ifndef QT_NO_NETWORKPROXY
746void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a)
747{
748 if (!httpReply)
749 return;
750
751#ifdef QHTTPTHREADDELEGATE_DEBUG
752 qDebug() << "QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
753#endif
754 // Ask the credential cache
755 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a);
756 if (!credential.isNull()) {
757 a->setUser(credential.user);
758 a->setPassword(credential.password);
759 }
760
761#ifndef QT_NO_NETWORKPROXY
762 // Disconnect this connection now since we only want to ask the authentication cache once.
763 QObject::disconnect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
764 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
765#endif
766}
767
768#endif
769
770QT_END_NAMESPACE
771
772#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 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)
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)