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 if (!notifiedFinished) {
176 emit error(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
177 emit downloadFinished();
178 }
179
180 // It could be that the main thread has asked us to shut down, so we need to delete the HTTP reply
181 if (httpReply) {
182 delete httpReply;
183 }
184
185 // Get the object cache that stores our QHttpNetworkConnection objects
186 // and release the entry for this QHttpNetworkConnection
187 if (connectionCacheExpiryTimeoutSeconds.value_or(0) < 0) {
188 delete httpConnection;
189 } else if (connections.hasLocalData() && !cacheKey.isEmpty()) {
190 connections.localData()->releaseEntry(cacheKey);
191 }
192}
193
194
195QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
196 QObject(parent)
197 , ssl(false)
198 , downloadBufferMaximumSize(0)
199 , readBufferMaxSize(0)
200 , bytesEmitted(0)
201 , pendingDownloadData()
202 , pendingDownloadProgress()
203 , synchronous(false)
205 , isPipeliningUsed(false)
206 , isHttp2Used(false)
207 , incomingContentLength(-1)
208 , removedContentLength(-1)
209 , incomingErrorCode(QNetworkReply::NoError)
210 , downloadBuffer()
211 , httpConnection(nullptr)
212 , httpReply(nullptr)
213 , synchronousRequestLoop(nullptr)
214{
215}
216
217// This is invoked as BlockingQueuedConnection from QNetworkAccessHttpBackend in the user thread
219{
220#ifdef QHTTPTHREADDELEGATE_DEBUG
221 qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId();
222#endif
223 synchronous = true;
224
225 QEventLoop synchronousRequestLoop;
226 QScopedValueRollback<QEventLoop*> guard(this->synchronousRequestLoop, &synchronousRequestLoop);
227
228 // Worst case timeout
229 QTimer::singleShot(30*1000, this, SLOT(abortRequest()));
230
231 QMetaObject::invokeMethod(this, "startRequest", Qt::QueuedConnection);
232 synchronousRequestLoop.exec();
233
234 connections.localData()->releaseEntry(cacheKey);
235 connections.setLocalData(nullptr);
236
237#ifdef QHTTPTHREADDELEGATE_DEBUG
238 qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId() << "finished";
239#endif
240}
241
242
243// This is invoked as QueuedConnection from QNetworkAccessHttpBackend in the user thread
244void QHttpThreadDelegate::startRequest()
245{
246#ifdef QHTTPTHREADDELEGATE_DEBUG
247 qDebug() << "QHttpThreadDelegate::startRequest() thread=" << QThread::currentThreadId();
248#endif
249 // Check QThreadStorage for the QNetworkAccessCache
250 // If not there, create this connection cache
251 if (!connections.hasLocalData()) {
252 connections.setLocalData(new QNetworkAccessCache());
253 }
254
255 // check if we have an open connection to this host
256 QUrl urlCopy = httpRequest.url();
257 const bool isLocalSocket = urlCopy.scheme().startsWith("unix"_L1);
258 if (!isLocalSocket)
259 urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
260
261 QHttpNetworkConnection::ConnectionType connectionType
262 = httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
263 : QHttpNetworkConnection::ConnectionTypeHTTP;
264 if (httpRequest.isHTTP2Direct()) {
265 Q_ASSERT(!httpRequest.isHTTP2Allowed());
266 connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2Direct;
267 }
268
269 // Use HTTP/1.1 if h2c is not allowed and we would otherwise choose to use it
270 if (!ssl && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
271 && !httpRequest.isH2cAllowed()) {
272 connectionType = QHttpNetworkConnection::ConnectionTypeHTTP;
273 }
274
275#if QT_CONFIG(ssl)
276 // See qnetworkreplyhttpimpl, delegate's initialization code.
277 Q_ASSERT(!ssl || incomingSslConfiguration);
278#endif // QT_CONFIG(ssl)
279
280 const bool isH2 = httpRequest.isHTTP2Allowed() || httpRequest.isHTTP2Direct();
281 if (isH2) {
282#if QT_CONFIG(ssl)
283 if (ssl) {
284 if (!httpRequest.isHTTP2Direct()) {
285 QList<QByteArray> protocols;
286 protocols << QSslConfiguration::ALPNProtocolHTTP2
287 << QSslConfiguration::NextProtocolHttp1_1;
288 incomingSslConfiguration->setAllowedNextProtocols(protocols);
289 }
290 urlCopy.setScheme(QStringLiteral("h2s"));
291 } else
292#endif // QT_CONFIG(ssl)
293 {
294 if (isLocalSocket)
295 urlCopy.setScheme(QStringLiteral("unix+h2"));
296 else
297 urlCopy.setScheme(QStringLiteral("h2"));
298 }
299 }
300
301 QString extraData = httpRequest.peerVerifyName();
302 if (isLocalSocket) {
303 if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
304 extraData = path;
305 }
306
307#ifndef QT_NO_NETWORKPROXY
308 if (transparentProxy.type() != QNetworkProxy::NoProxy)
309 cacheKey = makeCacheKey(urlCopy, &transparentProxy, httpRequest.peerVerifyName());
310 else if (cacheProxy.type() != QNetworkProxy::NoProxy)
311 cacheKey = makeCacheKey(urlCopy, &cacheProxy, httpRequest.peerVerifyName());
312 else
313#endif
314 cacheKey = makeCacheKey(urlCopy, nullptr, httpRequest.peerVerifyName());
315
316 // the http object is actually a QHttpNetworkConnection
317 if (connectionCacheExpiryTimeoutSeconds.value_or(0) >= 0)
319 connections.localData()->requestEntryNow(cacheKey));
320
321 if (!httpConnection) {
322
323 QString host = urlCopy.host();
324 // Update the host if a unix socket path or named pipe is used:
325 if (isLocalSocket) {
326 if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
327 host = path;
328 }
329
330 // no entry in cache; create an object
331 // the http object is actually a QHttpNetworkConnection
332 httpConnection = new QNetworkAccessCachedHttpConnection(
333 http1Parameters.numberOfConnectionsPerHost(), host, urlCopy.port(), ssl,
334 isLocalSocket, connectionType);
335 if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
336 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
337 httpConnection->setHttp2Parameters(http2Parameters);
338 }
339
340 httpConnection->setTcpKeepAliveParameters(tcpKeepAliveParameters);
341#ifndef QT_NO_SSL
342 // Set the QSslConfiguration from this QNetworkRequest.
343 if (ssl)
344 httpConnection->setSslConfiguration(*incomingSslConfiguration);
345#endif
346
347#ifndef QT_NO_NETWORKPROXY
348 httpConnection->setTransparentProxy(transparentProxy);
349 httpConnection->setCacheProxy(cacheProxy);
350#endif
351 httpConnection->setPeerVerifyName(httpRequest.peerVerifyName());
352 // cache the QHttpNetworkConnection corresponding to this cache key
353 if (connectionCacheExpiryTimeoutSeconds.value_or(0) >= 0)
354 connections.localData()->addEntry(cacheKey, httpConnection,
355 connectionCacheExpiryTimeoutSeconds);
356 } else {
357 if (httpRequest.withCredentials()) {
358 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), nullptr);
359 if (!credential.user.isEmpty() && !credential.password.isEmpty()) {
360 QAuthenticator auth;
361 auth.setUser(credential.user);
362 auth.setPassword(credential.password);
363 httpConnection->d_func()->copyCredentials(-1, &auth, false);
364 }
365 }
366 }
367
368 // Send the request to the connection
369 httpReply = httpConnection->sendRequest(httpRequest);
370 httpReply->setParent(this);
371
372 // Connect the reply signals that we need to handle and then forward
373 if (synchronous) {
374 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));
375 connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));
376 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
377 this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
378
379 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
380 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
381#ifndef QT_NO_NETWORKPROXY
382 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
383 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
384#endif
385
386 // Don't care about ignored SSL errors for now in the synchronous HTTP case.
387 } else if (!synchronous) {
388 connect(httpReply,SIGNAL(socketStartedConnecting()), this, SIGNAL(socketStartedConnecting()));
389 connect(httpReply,SIGNAL(requestSent()), this, SIGNAL(requestSent()));
390 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
391 connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
392 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
393 this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
394 // some signals are only interesting when normal asynchronous style is used
395 connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
396 connect(httpReply,SIGNAL(dataReadProgress(qint64,qint64)), this, SLOT(dataReadProgressSlot(qint64,qint64)));
397#ifndef QT_NO_SSL
398 connect(httpReply,SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
399 connect(httpReply,SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
400 connect(httpReply,SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
401 this, SLOT(preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator*)));
402#endif
403
404 // In the asynchronous HTTP case we can just forward those signals
405 // Connect the reply signals that we can directly forward
406 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
407 this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
408#ifndef QT_NO_NETWORKPROXY
409 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
410 this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
411#endif
412 }
413
414 connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
415 this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
416 if (httpReply->errorCode() != QNetworkReply::NoError) {
417 if (synchronous)
418 synchronousFinishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
419 else
420 finishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
421 }
422}
423
424// This gets called from the user thread or by the synchronous HTTP timeout timer
426{
427#ifdef QHTTPTHREADDELEGATE_DEBUG
428 qDebug() << "QHttpThreadDelegate::abortRequest() thread=" << QThread::currentThreadId() << "sync=" << synchronous;
429#endif
430 if (httpReply) {
431 httpReply->abort();
432 delete httpReply;
433 httpReply = nullptr;
434 }
435
436 // Got aborted by the timeout timer
437 if (synchronous) {
438 incomingErrorCode = QNetworkReply::TimeoutError;
439 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
440 } else {
441 //only delete this for asynchronous mode or QNetworkAccessHttpBackend will crash - see QNetworkAccessHttpBackend::postRequest()
442 this->deleteLater();
443 }
444}
445
447{
448#ifdef QHTTPTHREADDELEGATE_DEBUG
449 qDebug() << "QHttpThreadDelegate::readBufferSizeChanged() size " << size;
450#endif
451 if (httpReply) {
452 httpReply->setDownstreamLimited(size > 0);
453 httpReply->setReadBufferSize(size);
454 readBufferMaxSize = size;
455 }
456}
457
459{
460 if (readBufferMaxSize) {
461 bytesEmitted -= size;
462
463 QMetaObject::invokeMethod(this, "readyReadSlot", Qt::QueuedConnection);
464 }
465}
466
467void QHttpThreadDelegate::readyReadSlot()
468{
469 if (!httpReply)
470 return;
471
472 // Don't do in zerocopy case
473 if (!downloadBuffer.isNull())
474 return;
475
476 if (readBufferMaxSize) {
477 if (bytesEmitted < readBufferMaxSize) {
478 qint64 sizeEmitted = 0;
479 while (httpReply->readAnyAvailable() && (sizeEmitted < (readBufferMaxSize-bytesEmitted))) {
480 if (httpReply->sizeNextBlock() > (readBufferMaxSize-bytesEmitted)) {
481 sizeEmitted = readBufferMaxSize-bytesEmitted;
482 bytesEmitted += sizeEmitted;
483 pendingDownloadData->fetchAndAddRelease(1);
484 emit downloadData(httpReply->read(sizeEmitted));
485 } else {
486 sizeEmitted = httpReply->sizeNextBlock();
487 bytesEmitted += sizeEmitted;
488 pendingDownloadData->fetchAndAddRelease(1);
489 emit downloadData(httpReply->readAny());
490 }
491 }
492 } else {
493 // We need to wait until we empty data from the read buffer in the reply.
494 }
495
496 } else {
497 while (httpReply->readAnyAvailable()) {
498 pendingDownloadData->fetchAndAddRelease(1);
499 emit downloadData(httpReply->readAny());
500 }
501 }
502}
503
504static QString makeServerErrorString(int code, const QUrl &url, const QString &reasonPhrase)
505{
506 QString msg;
507 if (!reasonPhrase.isEmpty()) {
508 msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
509 "Error transferring %1 - server replied: %2"))
510 .arg(url.toString(), reasonPhrase);
511 } else {
512 msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
513 "Error transferring %1 - server replied with status code %2"))
514 .arg(url.toString(), QString::number(code));
515 }
516 return msg;
517}
518
520{
521 if (!httpReply)
522 return;
523
524#ifdef QHTTPTHREADDELEGATE_DEBUG
525 qDebug() << "QHttpThreadDelegate::finishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
526#endif
527
528 // If there is still some data left emit that now
529 while (httpReply->readAnyAvailable()) {
530 pendingDownloadData->fetchAndAddRelease(1);
531 emit downloadData(httpReply->readAny());
532 }
533
534#ifndef QT_NO_SSL
535 if (ssl)
536 emit sslConfigurationChanged(httpReply->sslConfiguration());
537#endif
538
539 if (httpReply->statusCode() >= 400) {
540 // it's an error reply
541 QString msg = makeServerErrorString(httpReply->statusCode(), httpRequest.url(),
542 httpReply->reasonPhrase());
543 emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
544 }
545
546 if (httpRequest.isFollowRedirects() && httpReply->isRedirecting())
547 emit redirected(httpReply->redirectUrl(), httpReply->statusCode(), httpReply->request().redirectCount() - 1);
548
549 emit downloadFinished();
550 notifiedFinished = true;
551
552 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
553 QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
554 httpReply = nullptr;
555}
556
558{
559 if (!httpReply)
560 return;
561
562#ifdef QHTTPTHREADDELEGATE_DEBUG
563 qDebug() << "QHttpThreadDelegate::synchronousFinishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
564#endif
565 if (httpReply->statusCode() >= 400) {
566 // it's an error reply
567 incomingErrorDetail = makeServerErrorString(httpReply->statusCode(), httpRequest.url(),
568 httpReply->reasonPhrase());
569 incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
570 }
571
572 isCompressed = httpReply->isCompressed();
573 synchronousDownloadData = httpReply->readAll();
574
575 notifiedFinished = true;
576 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
577 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
578 httpReply = nullptr;
579}
580
581void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
582{
583 if (!httpReply)
584 return;
585
586#ifdef QHTTPTHREADDELEGATE_DEBUG
587 qDebug() << "QHttpThreadDelegate::finishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
588#endif
589
590#ifndef QT_NO_SSL
591 if (ssl)
592 emit sslConfigurationChanged(httpReply->sslConfiguration());
593#endif
594 emit error(errorCode,detail);
595 emit downloadFinished();
596 notifiedFinished = true;
597
598 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
599 QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
600 httpReply = nullptr;
601}
602
603
604void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
605{
606 if (!httpReply)
607 return;
608
609#ifdef QHTTPTHREADDELEGATE_DEBUG
610 qDebug() << "QHttpThreadDelegate::synchronousFinishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
611#endif
612 incomingErrorCode = errorCode;
613 incomingErrorDetail = detail;
614
615 synchronousDownloadData = httpReply->readAll();
616
617 notifiedFinished = true;
618 QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
619 QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
620 httpReply = nullptr;
621}
622
624{
625 if (!httpReply)
626 return;
627
628#ifdef QHTTPTHREADDELEGATE_DEBUG
629 qDebug() << "QHttpThreadDelegate::headerChangedSlot() thread=" << QThread::currentThreadId();
630#endif
631
632#ifndef QT_NO_SSL
633 if (ssl)
634 emit sslConfigurationChanged(httpReply->sslConfiguration());
635#endif
636
637 // Is using a zerocopy buffer allowed by user and possible with this reply?
638 if (httpReply->supportsUserProvidedDownloadBuffer()
639 && (downloadBufferMaximumSize > 0) && (httpReply->contentLength() <= downloadBufferMaximumSize)) {
640 char *buf = new (std::nothrow) char[httpReply->contentLength()];
641 // in out of memory situations, don't use downloadBuffer.
642 if (buf) {
643 downloadBuffer = QSharedPointer<char>(buf, [](auto p) { delete[] p; });
644 httpReply->setUserProvidedDownloadBuffer(buf);
645 }
646 }
647
648 // We fetch this into our own
649 incomingHeaders = httpReply->header();
650 incomingStatusCode = httpReply->statusCode();
651 incomingReasonPhrase = httpReply->reasonPhrase();
652 isPipeliningUsed = httpReply->isPipeliningUsed();
653 incomingContentLength = httpReply->contentLength();
654 removedContentLength = httpReply->removedContentLength();
655 isHttp2Used = httpReply->isHttp2Used();
656 isCompressed = httpReply->isCompressed();
657
658 emit downloadMetaData(incomingHeaders,
659 incomingStatusCode,
660 incomingReasonPhrase,
661 isPipeliningUsed,
662 downloadBuffer,
663 incomingContentLength,
664 removedContentLength,
665 isHttp2Used,
666 isCompressed);
667}
668
670{
671 if (!httpReply)
672 return;
673
674#ifdef QHTTPTHREADDELEGATE_DEBUG
675 qDebug() << "QHttpThreadDelegate::synchronousHeaderChangedSlot() thread=" << QThread::currentThreadId();
676#endif
677 // Store the information we need in this object, the QNetworkAccessHttpBackend will later read it
678 incomingHeaders = httpReply->header();
679 incomingStatusCode = httpReply->statusCode();
680 incomingReasonPhrase = httpReply->reasonPhrase();
681 isPipeliningUsed = httpReply->isPipeliningUsed();
682 isHttp2Used = httpReply->isHttp2Used();
683 incomingContentLength = httpReply->contentLength();
684}
685
686
687void QHttpThreadDelegate::dataReadProgressSlot(qint64 done, qint64 total)
688{
689 // If we don't have a download buffer don't attempt to go this codepath
690 // It is not used by QNetworkAccessHttpBackend
691 if (downloadBuffer.isNull())
692 return;
693
694 pendingDownloadProgress->fetchAndAddRelease(1);
695 emit downloadProgress(done, total);
696}
697
698void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
699{
700 authenticationManager->cacheCredentials(request.url(), authenticator);
701}
702
703
704#ifndef QT_NO_SSL
706{
707 if (!httpReply)
708 return;
709
710 emit sslConfigurationChanged(httpReply->sslConfiguration());
711 emit encrypted();
712}
713
714void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
715{
716 if (!httpReply)
717 return;
718
719 emit sslConfigurationChanged(httpReply->sslConfiguration());
720
721 bool ignoreAll = false;
722 QList<QSslError> specificErrors;
723 emit sslErrors(errors, &ignoreAll, &specificErrors);
724 if (ignoreAll)
725 httpReply->ignoreSslErrors();
726 if (!specificErrors.isEmpty())
727 httpReply->ignoreSslErrors(specificErrors);
728}
729
730void QHttpThreadDelegate::preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator)
731{
732 if (!httpReply)
733 return;
734
735 emit preSharedKeyAuthenticationRequired(authenticator);
736}
737#endif
738
739void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)
740{
741 if (!httpReply)
742 return;
743
744 Q_UNUSED(request);
745#ifdef QHTTPTHREADDELEGATE_DEBUG
746 qDebug() << "QHttpThreadDelegate::synchronousAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
747#endif
748
749 // Ask the credential cache
750 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a);
751 if (!credential.isNull()) {
752 a->setUser(credential.user);
753 a->setPassword(credential.password);
754 }
755
756 // Disconnect this connection now since we only want to ask the authentication cache once.
757 QObject::disconnect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
758 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
759}
760
761#ifndef QT_NO_NETWORKPROXY
762void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a)
763{
764 if (!httpReply)
765 return;
766
767#ifdef QHTTPTHREADDELEGATE_DEBUG
768 qDebug() << "QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
769#endif
770 // Ask the credential cache
771 QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a);
772 if (!credential.isNull()) {
773 a->setUser(credential.user);
774 a->setPassword(credential.password);
775 }
776
777#ifndef QT_NO_NETWORKPROXY
778 // Disconnect this connection now since we only want to ask the authentication cache once.
779 QObject::disconnect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
780 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
781#endif
782}
783
784#endif
785
786QT_END_NAMESPACE
787
788#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)