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