Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qnetworkreplyhttpimpl.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
4//#define QNETWORKACCESSHTTPBACKEND_DEBUG
5
10#include "qnetworkrequest.h"
11#include "qnetworkreply.h"
12#include "qnetworkrequest_p.h"
13#include "qnetworkcookie.h"
14#include "qnetworkcookie_p.h"
15#include "QtCore/qdatetime.h"
16#include "QtCore/qelapsedtimer.h"
17#include "QtNetwork/qsslconfiguration.h"
19#include "qhsts_p.h"
20#include "qthread.h"
21#include "QtCore/qcoreapplication.h"
22
23#include <QtCore/private/qthread_p.h>
24#include <QtCore/private/qtools_p.h>
25
26#include "qnetworkcookiejar.h"
27#include "qnetconmonitor_p.h"
28
29#include "qnetworkreplyimpl_p.h"
30
31#include <string.h> // for strchr
32
34
35using namespace Qt::StringLiterals;
36using namespace QtMiscUtils;
37using namespace std::chrono_literals;
38
39class QNetworkProxy;
40
41static inline QByteArray rangeName() { return "Range"_ba; }
42static inline QByteArray cacheControlName() { return "Cache-Control"_ba; }
43static constexpr QByteArrayView bytesEqualPrefix() noexcept { return "bytes="; }
44
45// ### merge with nextField in cookiejar.cpp
46static QHash<QByteArray, QByteArray> parseHttpOptionHeader(QByteArrayView header)
47{
48 // The HTTP header is of the form:
49 // header = #1(directives)
50 // directives = token | value-directive
51 // value-directive = token "=" (token | quoted-string)
52 QHash<QByteArray, QByteArray> result;
53
54 int pos = 0;
55 while (true) {
56 // skip spaces
58 if (pos == header.size())
59 return result; // end of parsing
60
61 // pos points to a non-whitespace
62 int comma = header.indexOf(',', pos);
63 int equal = header.indexOf('=', pos);
64 if (comma == pos || equal == pos)
65 // huh? Broken header.
66 return result;
67
68 // The key name is delimited by either a comma, an equal sign or the end
69 // of the header, whichever comes first
70 int end = comma;
71 if (end == -1)
72 end = header.size();
73 if (equal != -1 && end > equal)
74 end = equal; // equal sign comes before comma/end
75 const auto key = header.sliced(pos, end - pos).trimmed();
76 pos = end + 1;
77
78 if (uint(equal) < uint(comma)) {
79 // case: token "=" (token | quoted-string)
80 // skip spaces
82 if (pos == header.size())
83 // huh? Broken header
84 return result;
85
87 value.reserve(header.size() - pos);
88 if (header.at(pos) == '"') {
89 // case: quoted-string
90 // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
91 // qdtext = <any TEXT except <">>
92 // quoted-pair = "\" CHAR
93 ++pos;
94 while (pos < header.size()) {
95 char c = header.at(pos);
96 if (c == '"') {
97 // end of quoted text
98 break;
99 } else if (c == '\\') {
100 ++pos;
101 if (pos >= header.size())
102 // broken header
103 return result;
104 c = header.at(pos);
105 }
106
107 value += c;
108 ++pos;
109 }
110 } else {
111 const auto isSeparator = [](char c) {
112 static const char separators[] = "()<>@,;:\\\"/[]?={}";
113 return isLWS(c) || strchr(separators, c) != nullptr;
114 };
115
116 // case: token
117 while (pos < header.size()) {
118 char c = header.at(pos);
119 if (isSeparator(c))
120 break;
121 value += c;
122 ++pos;
123 }
124 }
125
126 result.insert(key.toByteArray().toLower(), value);
127
128 // find the comma now:
129 comma = header.indexOf(',', pos);
130 if (comma == -1)
131 return result; // end of parsing
132 pos = comma + 1;
133 } else {
134 // case: token
135 // key is already set
136 result.insert(key.toByteArray().toLower(), QByteArray());
137 }
138 }
139}
140
144 QIODevice* outgoingData)
146{
149 d->manager = manager;
150 d->managerPrivate = manager->d_func();
151 d->request = request;
152 d->originalRequest = request;
153 d->operation = operation;
154 d->outgoingData = outgoingData;
155 d->url = request.url();
156#ifndef QT_NO_SSL
157 if (request.url().scheme() == "https"_L1)
158 d->sslConfiguration.reset(new QSslConfiguration(request.sslConfiguration()));
159#endif
160
163
164 // FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
166
167
168 // Internal code that does a HTTP reply for the synchronous Ajax
169 // in Qt WebKit.
170 QVariant synchronousHttpAttribute = request.attribute(
172 if (synchronousHttpAttribute.isValid()) {
173 d->synchronous = synchronousHttpAttribute.toBool();
174 if (d->synchronous && outgoingData) {
175 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
176 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
177 d->outgoingDataBuffer = std::make_shared<QRingBuffer>();
178 qint64 previousDataSize = 0;
179 do {
180 previousDataSize = d->outgoingDataBuffer->size();
181 d->outgoingDataBuffer->append(d->outgoingData->readAll());
182 } while (d->outgoingDataBuffer->size() != previousDataSize);
183 d->_q_startOperation();
184 return;
185 }
186 }
187
188
189 if (outgoingData) {
190 // there is data to be uploaded, e.g. HTTP POST.
191
192 if (!d->outgoingData->isSequential()) {
193 // fixed size non-sequential (random-access)
194 // just start the operation
195 QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
196 // FIXME make direct call?
197 } else {
198 bool bufferingDisallowed =
200 false).toBool();
201
202 if (bufferingDisallowed) {
203 // if a valid content-length header for the request was supplied, we can disable buffering
204 // if not, we will buffer anyway
205
206 const auto sizeOpt = QNetworkHeadersPrivate::toInt(
208 if (sizeOpt) {
209 QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
210 // FIXME make direct call?
211 } else {
212 d->state = d->Buffering;
213 QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
214 }
215 } else {
216 // _q_startOperation will be called when the buffering has finished.
217 d->state = d->Buffering;
218 QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
219 }
220 }
221 } else {
222 // No outgoing data (POST, ..)
223 d->_q_startOperation();
224 }
225}
226
228{
229 // This will do nothing if the request was already finished or aborted
231}
232
234{
236
237 if (d->state == QNetworkReplyPrivate::Aborted ||
239 return;
240
241 // According to the documentation close only stops the download
242 // by closing we can ignore the download part and continue uploading.
244
245 // call finished which will emit signals
246 // FIXME shouldn't this be emitted Queued?
247 d->error(OperationCanceledError, tr("Operation canceled"));
248 d->finished();
249}
250
252{
254 // FIXME
256 return;
257
259
260 if (d->state != QNetworkReplyPrivate::Finished) {
261 // call finished which will emit signals
262 // FIXME shouldn't this be emitted Queued?
263 d->error(OperationCanceledError, tr("Operation canceled"));
264 d->finished();
265 }
266
268
270}
271
273{
274 Q_D(const QNetworkReplyHttpImpl);
275
276 // if we load from cache device
277 if (d->cacheLoadDevice) {
278 return QNetworkReply::bytesAvailable() + d->cacheLoadDevice->bytesAvailable();
279 }
280
281 // zerocopy buffer
282 if (d->downloadZerocopyBuffer) {
283 return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
284 }
285
286 if (d->decompressHelper.isValid()) {
287 if (d->decompressHelper.isCountingBytes())
288 return QNetworkReply::bytesAvailable() + d->decompressHelper.uncompressedSize();
289 if (d->decompressHelper.hasData())
291 }
292
293 // normal buffer
295}
296
298{
299 // FIXME In the cache of a cached load or the zero-copy buffer we could actually be non-sequential.
300 // FIXME however this requires us to implement stuff like seek() too.
301 return true;
302}
303
305{
306 // FIXME At some point, this could return a proper value, e.g. if we're non-sequential.
307 return QNetworkReply::size();
308}
309
311{
313
314 // cacheload device
315 if (d->cacheLoadDevice) {
316 // FIXME bytesdownloaded, position etc?
317
318 qint64 ret = d->cacheLoadDevice->read(data, maxlen);
319 return ret;
320 }
321
322 // zerocopy buffer
323 if (d->downloadZerocopyBuffer) {
324 // FIXME bytesdownloaded, position etc?
325
326 qint64 howMuch = qMin(maxlen, (d->downloadBufferCurrentSize - d->downloadBufferReadPosition));
327 memcpy(data, d->downloadZerocopyBuffer + d->downloadBufferReadPosition, howMuch);
328 d->downloadBufferReadPosition += howMuch;
329 return howMuch;
330
331 }
332
333 if (d->decompressHelper.isValid() && (d->decompressHelper.hasData() || !isFinished())) {
334 if (maxlen == 0 || !d->decompressHelper.hasData())
335 return 0;
336 const qint64 bytesRead = d->decompressHelper.read(data, maxlen);
337 if (!d->decompressHelper.isValid()) {
339 QCoreApplication::translate("QHttp", "Decompression failed: %1")
340 .arg(d->decompressHelper.errorString()));
341 d->decompressHelper.clear();
342 return -1;
343 }
344 if (d->cacheSaveDevice) {
345 // Need to write to the cache now that we have the data
346 d->cacheSaveDevice->write(data, bytesRead);
347 // ... and if we've read everything then the cache can be closed.
348 if (isFinished() && !d->decompressHelper.hasData())
349 d->completeCacheSave();
350 }
351 // In case of buffer size restriction we need to emit that it has been emptied
352 qint64 wasBuffered = d->bytesBuffered;
353 d->bytesBuffered = 0;
354 if (readBufferSize())
355 emit readBufferFreed(wasBuffered);
356 return bytesRead;
357 }
358
359 // normal buffer
360 if (d->state == d->Finished || d->state == d->Aborted)
361 return -1;
362
363 qint64 wasBuffered = d->bytesBuffered;
364 d->bytesBuffered = 0;
365 if (readBufferSize())
366 emit readBufferFreed(wasBuffered);
367 return 0;
368}
369
376
378{
379 Q_D(const QNetworkReplyHttpImpl);
380
382 return true;
383
384 if (d->cacheLoadDevice)
385 return d->cacheLoadDevice->canReadLine();
386
387 if (d->downloadZerocopyBuffer)
388 return memchr(d->downloadZerocopyBuffer + d->downloadBufferReadPosition, '\n', d->downloadBufferCurrentSize - d->downloadBufferReadPosition);
389
390 return false;
391}
392
393#ifndef QT_NO_SSL
395{
397 Q_ASSERT(d->managerPrivate);
398
399 if (d->managerPrivate->stsEnabled && d->managerPrivate->stsCache.isKnownHost(url())) {
400 // We cannot ignore any Security Transport-related errors for this host.
401 return;
402 }
403
404 d->pendingIgnoreAllSslErrors = true;
405}
406
408{
410 Q_ASSERT(d->managerPrivate);
411
412 if (d->managerPrivate->stsEnabled && d->managerPrivate->stsCache.isKnownHost(url())) {
413 // We cannot ignore any Security Transport-related errors for this host.
414 return;
415 }
416
417 // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
418 // is called before QNetworkAccessManager::get() (or post(), etc.)
419 d->pendingIgnoreSslErrorsList = errors;
420}
421
423{
424 // Setting a SSL configuration on a reply is not supported. The user needs to set
425 // her/his QSslConfiguration on the QNetworkRequest.
426 Q_UNUSED(newconfig);
427}
428
430{
431 Q_D(const QNetworkReplyHttpImpl);
432 if (d->sslConfiguration.data())
433 configuration = *d->sslConfiguration;
434 else
435 configuration = request().sslConfiguration();
436}
437#endif
438
442 , managerPrivate(nullptr)
443 , synchronous(false)
444 , state(Idle)
445 , statusCode(0)
446 , uploadByteDevicePosition(false)
447 , uploadDeviceChoking(false)
448 , outgoingData(nullptr)
449 , bytesUploaded(-1)
450 , cacheLoadDevice(nullptr)
451 , loadingFromCache(false)
452 , cacheSaveDevice(nullptr)
453 , cacheEnabled(false)
454 , resumeOffset(0)
455 , bytesDownloaded(0)
456 , bytesBuffered(0)
457 , transferTimeout(nullptr)
458 , downloadBufferReadPosition(0)
459 , downloadBufferCurrentSize(0)
460 , downloadZerocopyBuffer(nullptr)
461 , pendingDownloadDataEmissions(std::make_shared<QAtomicInt>())
462 , pendingDownloadProgressEmissions(std::make_shared<QAtomicInt>())
463 #ifndef QT_NO_SSL
464 , pendingIgnoreAllSslErrors(false)
465 #endif
466
467{
468}
469
473
474/*
475 For a given httpRequest
476 1) If AlwaysNetwork, return
477 2) If we have a cache entry for this url populate headers so the server can return 304
478 3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true
479 */
481{
482 QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
484
485 auto requestHeaders = request.headers();
486 if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
487 // If the request does not already specify preferred cache-control
488 // force reload from the network and tell any caching proxy servers to reload too
489 if (!requestHeaders.contains(QHttpHeaders::WellKnownHeader::CacheControl)) {
490 const auto noCache = "no-cache"_ba;
492 httpRequest.setHeaderField("Pragma"_ba, noCache);
493 }
494 return false;
495 }
496
497 // The disk cache API does not currently support partial content retrieval.
498 // That is why we don't use the disk cache for any such requests.
499 if (requestHeaders.contains(QHttpHeaders::WellKnownHeader::Range))
500 return false;
501
503 if (!nc)
504 return false; // no local cache
505
506 QNetworkCacheMetaData metaData = nc->metaData(httpRequest.url());
507 if (!metaData.isValid())
508 return false; // not in cache
509
510 if (!metaData.saveToDisk())
511 return false;
512
513 QHttpHeaders cacheHeaders = metaData.headers();
514
515 const auto sizeOpt = QNetworkHeadersPrivate::toInt(
517 if (sizeOpt) {
518 std::unique_ptr<QIODevice> data(nc->data(httpRequest.url()));
519 if (!data || data->size() < sizeOpt.value())
520 return false; // The data is smaller than the content-length specified
521 }
522
523 auto value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::ETag);
524 if (!value.empty())
525 httpRequest.setHeaderField("If-None-Match"_ba, value.toByteArray());
526
527 QDateTime lastModified = metaData.lastModified();
528 if (lastModified.isValid())
529 httpRequest.setHeaderField("If-Modified-Since"_ba, QNetworkHeadersPrivate::toHttpDate(lastModified));
530
532 if (!value.empty()) {
533 QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(value);
534 if (cacheControl.contains("must-revalidate"_ba))
535 return false;
536 if (cacheControl.contains("no-cache"_ba))
537 return false;
538 }
539
540 QDateTime currentDateTime = QDateTime::currentDateTimeUtc();
541 QDateTime expirationDate = metaData.expirationDate();
542
543 bool response_is_fresh;
544 if (!expirationDate.isValid()) {
545 /*
546 * age_value
547 * is the value of Age: header received by the cache with
548 * this response.
549 * date_value
550 * is the value of the origin server's Date: header
551 * request_time
552 * is the (local) time when the cache made the request
553 * that resulted in this cached response
554 * response_time
555 * is the (local) time when the cache received the
556 * response
557 * now
558 * is the current (local) time
559 */
560 const auto ageOpt = QNetworkHeadersPrivate::toInt(
561 cacheHeaders.value(QHttpHeaders::WellKnownHeader::Age));
562 const qint64 age_value = ageOpt.value_or(0);
563
564 QDateTime dateHeader;
565 qint64 date_value = 0;
566 value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::Date);
567 if (!value.empty()) {
569 date_value = dateHeader.toSecsSinceEpoch();
570 }
571
572 qint64 now = currentDateTime.toSecsSinceEpoch();
573 qint64 request_time = now;
574 qint64 response_time = now;
575
576 // Algorithm from RFC 2616 section 13.2.3
577 qint64 apparent_age = qMax<qint64>(0, response_time - date_value);
578 qint64 corrected_received_age = qMax(apparent_age, age_value);
579 qint64 response_delay = response_time - request_time;
580 qint64 corrected_initial_age = corrected_received_age + response_delay;
581 qint64 resident_time = now - response_time;
582 qint64 current_age = corrected_initial_age + resident_time;
583
584 qint64 freshness_lifetime = 0;
585
586 // RFC 2616 13.2.4 Expiration Calculations
587 if (lastModified.isValid() && dateHeader.isValid()) {
588 qint64 diff = lastModified.secsTo(dateHeader);
589 freshness_lifetime = diff / 10;
590 const auto warningHeader = "Warning"_ba;
591 if (httpRequest.headerField(warningHeader).isEmpty()) {
592 QDateTime dt = currentDateTime.addSecs(current_age);
593 if (currentDateTime.daysTo(dt) > 1)
594 httpRequest.setHeaderField(warningHeader, "113"_ba);
595 }
596 }
597
598 // the cache-saving code below sets the freshness_lifetime with (dateHeader - last_modified) / 10
599 // if "last-modified" is present, or to Expires otherwise
600 response_is_fresh = (freshness_lifetime > current_age);
601 } else {
602 // expiration date was calculated earlier (e.g. when storing object to the cache)
603 response_is_fresh = currentDateTime.secsTo(expirationDate) >= 0;
604 }
605
606 if (!response_is_fresh)
607 return false;
608
609#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
610 qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
611#endif
612 return sendCacheContents(metaData);
613}
614
627
629{
631
632 QThread *thread = nullptr;
633 if (synchronous) {
634 // A synchronous HTTP request uses its own thread
635 thread = new QThread();
636 thread->setObjectName(QStringLiteral("Qt HTTP synchronous thread"));
637 QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
638 thread->start();
639 } else {
640 // We use the manager-global thread.
641 // At some point we could switch to having multiple threads if it makes sense.
642 thread = managerPrivate->createThread();
643 }
644
645 QUrl url = newHttpRequest.url();
647 httpRequest.setRedirectCount(newHttpRequest.maximumRedirectsAllowed());
648
649 QString scheme = url.scheme();
650 bool ssl = (scheme == "https"_L1 || scheme == "preconnect-https"_L1);
652 httpRequest.setSsl(ssl);
653
654 bool preConnect = (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1);
655 httpRequest.setPreConnect(preConnect);
656
657#ifndef QT_NO_NETWORKPROXY
658 QNetworkProxy transparentProxy, cacheProxy;
659
660 // FIXME the proxy stuff should be done in the HTTP thread
661 const auto proxies = managerPrivate->queryProxy(QNetworkProxyQuery(newHttpRequest.url()));
662 for (const QNetworkProxy &p : proxies) {
663 // use the first proxy that works
664 // for non-encrypted connections, any transparent or HTTP proxy
665 // for encrypted, only transparent proxies
666 if (!ssl
667 && (p.capabilities() & QNetworkProxy::CachingCapability)
668 && (p.type() == QNetworkProxy::HttpProxy ||
670 cacheProxy = p;
671 transparentProxy = QNetworkProxy::NoProxy;
672 break;
673 }
674 if (p.isTransparentProxy()) {
675 transparentProxy = p;
676 cacheProxy = QNetworkProxy::NoProxy;
677 break;
678 }
679 }
680
681 // check if at least one of the proxies
682 if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
683 cacheProxy.type() == QNetworkProxy::DefaultProxy) {
684 // unsuitable proxies
687 Q_ARG(QString, QNetworkReplyHttpImpl::tr("No suitable proxy found")));
689 return;
690 }
691#endif
692
693 auto redirectPolicy = QNetworkRequest::NoLessSafeRedirectPolicy;
694 const QVariant value = newHttpRequest.attribute(QNetworkRequest::RedirectPolicyAttribute);
695 if (value.isValid())
696 redirectPolicy = qvariant_cast<QNetworkRequest::RedirectPolicy>(value);
697
698 httpRequest.setRedirectPolicy(redirectPolicy);
699
700 httpRequest.setPriority(convert(newHttpRequest.priority()));
701 loadingFromCache = false;
702
703 switch (operation) {
706 // If the request has a body, createUploadByteDevice() and don't use caching
707 if (outgoingData) {
711 return; // no need to send the request! :)
712 }
713 break;
714
718 return; // no need to send the request! :)
719 break;
720
725 break;
726
731 break;
732
736 break;
737
739 invalidateCache(); // for safety reasons, we don't know what the operation does
742 httpRequest.setCustomVerb(newHttpRequest.attribute(
744 break;
745
746 default:
747 break; // can't happen
748 }
749
750 QHttpHeaders newRequestHeaders = newHttpRequest.headers();
751 if (resumeOffset != 0) {
752 if (newRequestHeaders.contains(QHttpHeaders::WellKnownHeader::Range)) {
753 // Need to adjust resume offset for user specified range
754
755 // We've already verified that requestRange starts with "bytes=", see canResume.
756 const auto rangeHeader = newRequestHeaders.value(QHttpHeaders::WellKnownHeader::Range);
757 const auto requestRange = QByteArrayView(rangeHeader).mid(bytesEqualPrefix().size());
758
759 newRequestHeaders.removeAll(QHttpHeaders::WellKnownHeader::Range);
760
761 int index = requestRange.indexOf('-');
762
763 quint64 requestStartOffset = requestRange.left(index).toULongLong();
764 quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
765
766 // In case an end offset is not given it is skipped from the request range
767 QByteArray newRange = bytesEqualPrefix() + QByteArray::number(resumeOffset + requestStartOffset) +
768 '-' + (requestEndOffset ? QByteArray::number(requestEndOffset) : QByteArray());
769
771 } else {
773 }
774 }
775
776 for (int i = 0; i < newRequestHeaders.size(); i++) {
777 const auto name = newRequestHeaders.nameAt(i);
778 const auto value = newRequestHeaders.valueAt(i);
779 httpRequest.setHeaderField(QByteArray(name.data(), name.size()), value.toByteArray());
780 }
781
782 if (newHttpRequest.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool())
784
786 allowed.isValid() && allowed.canConvert<bool>()) {
787 httpRequest.setHTTP2Allowed(allowed.value<bool>());
788 }
790 // ### Qt7: Stop checking the environment variable
791 if (h2cAttribute.toBool()
792 || (!h2cAttribute.isValid() && qEnvironmentVariableIsSet("QT_NETWORK_H2C_ALLOWED"))) {
794 }
795
797 // Intentionally mutually exclusive - cannot be both direct and 'allowed'
800 }
801
802 if (static_cast<QNetworkRequest::LoadControl>
803 (newHttpRequest.attribute(QNetworkRequest::AuthenticationReuseAttribute,
806
809
810 httpRequest.setPeerVerifyName(newHttpRequest.peerVerifyName());
811
812 // Create the HTTP thread delegate
814 // Propagate Http/2 settings:
815 delegate->http2Parameters = request.http2Configuration();
816 delegate->http1Parameters = request.http1Configuration();
817
820
821 // For the synchronous HTTP, this is the normal way the delegate gets deleted
822 // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
823 QMetaObject::Connection threadFinishedConnection =
824 QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
825
826 // QTBUG-88063: When 'delegate' is deleted the connection will be added to 'thread''s orphaned
827 // connections list. This orphaned list will be cleaned up next time 'thread' emits a signal,
828 // unfortunately that's the finished signal. It leads to a soft-leak so we do this to disconnect
829 // it on deletion so that it cleans up the orphan immediately.
830 QObject::connect(delegate, &QObject::destroyed, delegate, [threadFinishedConnection]() {
831 if (bool(threadFinishedConnection))
832 QObject::disconnect(threadFinishedConnection);
833 });
834
835 // Set the properties it needs
836 delegate->httpRequest = httpRequest;
837#ifndef QT_NO_NETWORKPROXY
838 delegate->cacheProxy = cacheProxy;
839 delegate->transparentProxy = transparentProxy;
840#endif
841 delegate->ssl = ssl;
842#ifndef QT_NO_SSL
843 if (ssl)
844 delegate->incomingSslConfiguration.reset(new QSslConfiguration(newHttpRequest.sslConfiguration()));
845#endif
846
847 // Do we use synchronous HTTP?
848 delegate->synchronous = synchronous;
849
850 // The authentication manager is used to avoid the BlockingQueuedConnection communication
851 // from HTTP thread to user thread in some cases.
853
854 if (!synchronous) {
855 // Tell our zerocopy policy to the delegate
856 QVariant downloadBufferMaximumSizeAttribute = newHttpRequest.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
857 if (downloadBufferMaximumSizeAttribute.isValid()) {
858 delegate->downloadBufferMaximumSize = downloadBufferMaximumSizeAttribute.toLongLong();
859 } else {
860 // If there is no MaximumDownloadBufferSizeAttribute set (which is for the majority
861 // of QNetworkRequest) then we can assume we'll do it anyway for small HTTP replies.
862 // This helps with performance and memory fragmentation.
863 delegate->downloadBufferMaximumSize = 128*1024;
864 }
865
866
867 // These atomic integers are used for signal compression
870
871 // Connect the signals of the delegate to us
872 QObject::connect(delegate, SIGNAL(downloadData(QByteArray)),
875 QObject::connect(delegate, SIGNAL(downloadFinished()),
876 q, SLOT(replyFinished()),
884 QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
890 QObject::connect(delegate, SIGNAL(redirected(QUrl,int,int)),
891 q, SLOT(onRedirected(QUrl,int,int)),
893
894#ifndef QT_NO_SSL
895 QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
898#endif
899 // Those need to report back, therefore BlockingQueuedConnection
900 QObject::connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
903#ifndef QT_NO_NETWORKPROXY
907#endif
908#ifndef QT_NO_SSL
909 QObject::connect(delegate, SIGNAL(encrypted()), q, SLOT(replyEncrypted()),
911 QObject::connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
912 q, SLOT(replySslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
914 QObject::connect(delegate, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
917#endif
918 // This signal we will use to start the request.
919 QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
920 QObject::connect(q, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
921
922 // To throttle the connection.
923 QObject::connect(q, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64)));
924 QObject::connect(q, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64)));
925
926 if (uploadByteDevice) {
927 QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =
929 forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()
930 delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
931
932 // If the device in the user thread claims it has more data, keep the flow to HTTP thread going
933 QObject::connect(uploadByteDevice.get(), SIGNAL(readyRead()),
936
937 // From user thread to http thread:
938 QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)),
939 forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection);
940 QObject::connect(uploadByteDevice.get(), SIGNAL(readyRead()),
941 forwardUploadDevice, SIGNAL(readyRead()),
943
944 // From http thread to user thread:
945 QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
947 QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64,qint64)),
949 QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
950 q, SLOT(resetUploadDataSlot(bool*)),
951 Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
952 }
953 } else if (synchronous) {
954 QObject::connect(q, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);
955
956 if (uploadByteDevice) {
957 // For the synchronous HTTP use case the use thread (this one here) is blocked
958 // so we cannot use the asynchronous upload architecture.
959 // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
960 // use the uploadByteDevice provided to us by the QNetworkReplyImpl.
961 // The code that is in start() makes sure it is safe to use from a thread
962 // since it only wraps a QRingBuffer
964 }
965 }
966
967
968 // Move the delegate to the http thread
969 delegate->moveToThread(thread);
970 // This call automatically moves the uploadDevice too for the asynchronous case.
971
972 // Prepare timers for progress notifications
975
976 // Send an signal to the delegate so it starts working in the other thread
977 if (synchronous) {
978 emit q->startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
979
981 (delegate->incomingHeaders,
982 delegate->incomingStatusCode,
983 delegate->incomingReasonPhrase,
984 delegate->isPipeliningUsed,
985 QSharedPointer<char>(),
986 delegate->incomingContentLength,
987 delegate->removedContentLength,
988 delegate->isHttp2Used,
989 delegate->isCompressed);
991
993 httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
994
995 thread->quit();
996 thread->wait(QDeadlineTimer(5000));
997 if (thread->isFinished())
998 delete thread;
999 else
1000 QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
1001
1002 finished();
1003 } else {
1004 emit q->startHttpRequest(); // Signal to the HTTP thread and go back to user.
1005 }
1006}
1007
1014
1016{
1018
1019 // The disk cache does not support partial content, so don't even try to
1020 // save any such content into the cache.
1021 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
1022 cacheEnabled = false;
1023 return;
1024 }
1025
1026 // save the meta data
1027 QNetworkCacheMetaData metaData;
1028 metaData.setUrl(url);
1029 metaData = fetchCacheMetaData(metaData);
1030
1031 // save the redirect request also in the cache
1032 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
1033 if (redirectionTarget.isValid()) {
1036 metaData.setAttributes(attributes);
1037 }
1038
1040
1041 if (cacheSaveDevice)
1042 q->connect(cacheSaveDevice, SIGNAL(aboutToClose()), SLOT(_q_cacheSaveDeviceAboutToClose()));
1043
1046 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
1047 "class %s probably needs to be fixed",
1048 managerPrivate->networkCache->metaObject()->className());
1049
1051 cacheSaveDevice = nullptr;
1052 cacheEnabled = false;
1053 }
1054}
1055
1057{
1059
1060 // If we're closed just ignore this data
1061 if (!q->isOpen())
1062 return;
1063
1064 // cache this, we need it later and it's invalidated when dealing with compressed data
1065 auto dataSize = d.size();
1066
1069
1070 if (decompressHelper.isValid()) {
1071 qint64 uncompressedBefore = -1;
1073 uncompressedBefore = decompressHelper.uncompressedSize();
1074
1075 decompressHelper.feed(std::move(d));
1076
1077 if (!decompressHelper.isValid()) {
1079 QCoreApplication::translate("QHttp", "Decompression failed: %1")
1082 return;
1083 }
1084
1085 if (!isHttpRedirectResponse()) {
1087 bytesDownloaded += (decompressHelper.uncompressedSize() - uncompressedBefore);
1089 }
1090
1091 if (synchronous) {
1092 d = QByteArray();
1093 const qsizetype increments = 16 * 1024;
1094 qint64 bytesRead = 0;
1095 while (decompressHelper.hasData()) {
1096 quint64 nextSize = quint64(d.size()) + quint64(increments);
1097 if (nextSize > quint64(std::numeric_limits<QByteArray::size_type>::max())) {
1100 "Data downloaded is too large to store"));
1102 return;
1103 }
1104 d.resize(nextSize);
1105 bytesRead += decompressHelper.read(d.data() + bytesRead, increments);
1106 if (!decompressHelper.isValid()) {
1108 QCoreApplication::translate("QHttp", "Decompression failed: %1")
1111 return;
1112 }
1113 }
1114 d.resize(bytesRead);
1115 // we're synchronous so we're not calling this function again; reset the decompressHelper
1117 }
1118 }
1119
1120 // This is going to look a little strange. When downloading data while a
1121 // HTTP redirect is happening (and enabled), we write the redirect
1122 // response to the cache. However, we do not append it to our internal
1123 // buffer as that will contain the response data only for the final
1124 // response
1125 // Note: For compressed data this is done in readData()
1128 }
1129
1130 // if decompressHelper is valid then we have compressed data, and this is handled above
1132 buffer.append(d);
1135 }
1137
1138 int pendingSignals = pendingDownloadDataEmissions->fetchAndSubAcquire(1) - 1;
1139 if (pendingSignals > 0) {
1140 // Some more signal emissions to this slot are pending.
1141 // Instead of writing the downstream data, we wait
1142 // and do it in the next call we get
1143 // (signal comppression)
1144 return;
1145 }
1146
1148 return;
1149
1150 // This can occur when downloading compressed data as some of the data may be the content
1151 // encoding's header. Don't emit anything for this.
1154 emit q->readBufferFreed(dataSize);
1155 return;
1156 }
1158
1159 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
1161
1162 emit q->readyRead();
1163 // emit readyRead before downloadProgress in case this will cause events to be
1164 // processed and we get into a recursive call (as in QProgressDialog).
1168 emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
1169 }
1170}
1171
1173{
1174 // We are already loading from cache, we still however
1175 // got this signal because it was posted already
1176 if (loadingFromCache)
1177 return;
1178
1179 finished();
1180}
1181
1183{
1184 // HTTP status code can be used to decide if we can redirect with a GET
1185 // operation or not. See http://www.ietf.org/rfc/rfc2616.txt [Sec 10.3] for
1186 // more details
1187
1188 // We MUST keep using the verb that was used originally when being redirected with 307 or 308.
1189 if (httpStatus == 307 || httpStatus == 308)
1190 return currentOp;
1191
1192 switch (currentOp) {
1195 default:
1196 break;
1197 }
1198 // Use GET for everything else.
1200}
1201
1206
1208 const QUrl &url,
1209 int maxRedirectsRemaining)
1210{
1211 QNetworkRequest newRequest(originalRequest);
1212 newRequest.setUrl(url);
1213 newRequest.setMaximumRedirectsAllowed(maxRedirectsRemaining);
1214
1215 return newRequest;
1216}
1217
1218void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int httpStatus, int maxRedirectsRemaining)
1219{
1223
1224 if (isFinished)
1225 return;
1226
1227 const QString schemeBefore(url.scheme());
1228 if (httpRequest.isFollowRedirects()) // update the reply's url as it could've changed
1229 url = redirectUrl;
1230
1231 const bool wasLocalSocket = schemeBefore.startsWith("unix"_L1);
1232 if (!wasLocalSocket && managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) {
1233 // RFC6797, 8.3:
1234 // The UA MUST replace the URI scheme with "https" [RFC2818],
1235 // and if the URI contains an explicit port component of "80",
1236 // then the UA MUST convert the port component to be "443", or
1237 // if the URI contains an explicit port component that is not
1238 // equal to "80", the port component value MUST be preserved;
1239 // otherwise, if the URI does not contain an explicit port
1240 // component, the UA MUST NOT add one.
1241 url.setScheme("https"_L1);
1242 if (url.port() == 80)
1243 url.setPort(443);
1244 }
1245
1246 // Just to be on the safe side for local sockets, any changes to the scheme
1247 // are considered less safe
1248 const bool changingLocalScheme = wasLocalSocket && url.scheme() != schemeBefore;
1249 const bool isLessSafe = changingLocalScheme
1250 || (schemeBefore == "https"_L1 && url.scheme() == "http"_L1);
1253 QCoreApplication::translate("QHttp", "Insecure redirect"));
1254 return;
1255 }
1256
1257 // If the original operation was a GET with a body and the status code is either
1258 // 307 or 308 then keep the message body
1259 const bool getOperationKeepsBody = (operation == QNetworkAccessManager::GetOperation)
1260 && (httpStatus == 307 || httpStatus == 308);
1261
1262 redirectRequest = createRedirectRequest(originalRequest, url, maxRedirectsRemaining);
1264
1265 // Clear stale headers, the relevant ones get set again later
1267 auto newHeaders = redirectRequest.headers();
1269 || operation == QNetworkAccessManager::HeadOperation) && !getOperationKeepsBody) {
1270 // possibly changed from not-GET/HEAD to GET/HEAD, make sure to get rid of upload device
1271 uploadByteDevice.reset();
1273 if (outgoingData) {
1276 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q,
1278 }
1279 outgoingData = nullptr;
1281 // We need to explicitly unset these headers so they're not reapplied to the httpRequest
1283 newHeaders.removeAll(QHttpHeaders::WellKnownHeader::ContentType);
1284 }
1285
1286 if (const QNetworkCookieJar *const cookieJar = manager->cookieJar()) {
1287 auto cookies = cookieJar->cookiesForUrl(url);
1288 if (!cookies.empty()) {
1289 auto cookieHeader = QNetworkHeadersPrivate::fromCookieList(cookies);
1290 newHeaders.replaceOrAppend(QHttpHeaders::WellKnownHeader::Cookie, cookieHeader);
1291 }
1292 }
1293
1294 redirectRequest.setHeaders(std::move(newHeaders));
1295
1298
1299 emit q->redirected(url);
1300}
1301
1316
1317static constexpr QLatin1StringView locationHeader() noexcept { return "location"_L1; }
1318
1320{
1322 switch (statusCode) {
1323 case 301: // Moved Permanently
1324 case 302: // Found
1325 case 303: // See Other
1326 case 307: // Temporary Redirect
1327 case 308: // Permanent Redirect
1328 // What do we do about the caching of the HTML note?
1329 // The response to a 303 MUST NOT be cached, while the response to
1330 // all of the others is cacheable if the headers indicate it to be
1331 QByteArrayView header = q->headers().value(locationHeader());
1333 if (!url.isValid())
1336 }
1337}
1338
1340 int sc, const QString &rp, bool pu,
1341 QSharedPointer<char> db,
1342 qint64 contentLength,
1343 qint64 removedContentLength,
1344 bool h2Used, bool isCompressed)
1345{
1347 Q_UNUSED(contentLength);
1348
1349 statusCode = sc;
1350 reasonPhrase = rp;
1351
1352#ifndef QT_NO_SSL
1353 // We parse this header only if we're using secure transport:
1354 //
1355 // RFC6797, 8.1
1356 // If an HTTP response is received over insecure transport, the UA MUST
1357 // ignore any present STS header field(s).
1358 if (url.scheme() == "https"_L1 && managerPrivate->stsEnabled)
1360#endif
1361 // Download buffer
1362 if (!db.isNull()) {
1367 }
1368
1370 q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, h2Used);
1371
1372 // A user having manually defined which encodings they accept is, for
1373 // somwehat unknown (presumed legacy compatibility) reasons treated as
1374 // disabling our decompression:
1376 const bool shouldDecompress = isCompressed && autoDecompress;
1377 // reconstruct the HTTP header
1378 auto h = q->headers();
1379 for (qsizetype i = 0; i < hm.size(); ++i) {
1380 const auto key = hm.nameAt(i);
1381 const auto originValue = hm.valueAt(i);
1382
1383 // Reset any previous "location" header set in the reply. In case of
1384 // redirects, we don't want to 'append' multiple location header values,
1385 // rather we keep only the latest one
1386 if (key.compare(locationHeader(), Qt::CaseInsensitive) == 0)
1387 h.removeAll(key);
1388
1389 if (shouldDecompress && !decompressHelper.isValid() && key == "content-encoding"_L1) {
1390 if (!synchronous) // with synchronous all the data is expected to be handled at once
1392
1393 if (!decompressHelper.setEncoding(originValue)) {
1395 QCoreApplication::translate("QHttp", "Failed to initialize decompression: %1")
1397 return;
1398 }
1400 request.decompressedSafetyCheckThreshold());
1401 }
1402
1403 h.append(key, originValue);
1404 }
1405 q->setHeaders(std::move(h));
1406
1409 if (removedContentLength != -1)
1410 q->setAttribute(QNetworkRequest::OriginalContentLengthAttribute, removedContentLength);
1411
1412 // is it a redirection?
1415
1416 if (statusCode >= 500 && statusCode < 600) {
1418 if (nc) {
1419 QNetworkCacheMetaData metaData = nc->metaData(httpRequest.url());
1421 bool mustReValidate = false;
1422 if (!value.empty()) {
1423 QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(value);
1424 if (cacheControl.contains("must-revalidate"_ba))
1425 mustReValidate = true;
1426 }
1427 if (!mustReValidate && sendCacheContents(metaData))
1428 return;
1429 }
1430 }
1431
1432 if (statusCode == 304) {
1433#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1434 qDebug() << "Received a 304 from" << request.url();
1435#endif
1437 if (nc) {
1438 QNetworkCacheMetaData oldMetaData = nc->metaData(httpRequest.url());
1439 QNetworkCacheMetaData metaData = fetchCacheMetaData(oldMetaData);
1440 if (oldMetaData != metaData)
1441 nc->updateMetaData(metaData);
1442 if (sendCacheContents(metaData))
1443 return;
1444 }
1445 }
1446
1447
1448 if (statusCode != 304 && statusCode != 303) {
1449 if (!isCachingEnabled())
1450 setCachingEnabled(true);
1451 }
1452
1454}
1455
1457{
1459
1460 // If we're closed just ignore this data
1461 if (!q->isOpen())
1462 return;
1463
1464 // we can be sure here that there is a download buffer
1465
1466 int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1;
1467 if (pendingSignals > 0) {
1468 // Let's ignore this signal and look at the next one coming in
1469 // (signal comppression)
1470 return;
1471 }
1472
1473 if (!q->isOpen())
1474 return;
1475
1476 if (cacheEnabled && isCachingAllowed() && bytesReceived == bytesTotal) {
1477 // Write everything in one go if we use a download buffer. might be more performant.
1479 // need to check again if cache enabled and device exists
1482 // FIXME where is it closed?
1483 }
1484
1486 return;
1487
1488 bytesDownloaded = bytesReceived;
1490
1491 downloadBufferCurrentSize = bytesReceived;
1492
1493 // Only emit readyRead when actual data is there
1494 // emit readyRead before downloadProgress in case this will cause events to be
1495 // processed and we get into a recursive call (as in QProgressDialog).
1496 if (bytesDownloaded > 0)
1497 emit q->readyRead();
1500 emit q->downloadProgress(bytesDownloaded, bytesTotal);
1501 }
1502}
1503
1509
1510#ifndef QT_NO_NETWORKPROXY
1516#endif
1517
1519 const QString &errorString)
1520{
1521#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1522 qDebug() << "http error!" << errorCode << errorString;
1523#endif
1524
1525 // FIXME?
1527}
1528
1529#ifndef QT_NO_SSL
1531{
1533 emit q->encrypted();
1534}
1535
1537 const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored)
1538{
1540 emit q->sslErrors(list);
1541 // Check if the callback set any ignore and return this here to http thread
1543 *ignoreAll = true;
1545 *toBeIgnored = pendingIgnoreSslErrorsList;
1546}
1547
1549{
1550 // Receiving the used SSL configuration from the HTTP thread
1551 if (sslConfiguration.data())
1552 *sslConfiguration = newSslConfiguration;
1553 else
1554 sslConfiguration.reset(new QSslConfiguration(newSslConfiguration));
1555}
1556
1558{
1560 emit q->preSharedKeyAuthenticationRequired(authenticator);
1561}
1562#endif
1563
1564// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1566{
1567 *r = uploadByteDevice->reset();
1568 if (*r) {
1569 // reset our own position which is used for the inter-thread communication
1571 }
1572}
1573
1574// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1576{
1577 if (!uploadByteDevice) // uploadByteDevice is no longer available
1578 return;
1579
1580 if (uploadByteDevicePosition + amount != pos) {
1581 // Sanity check, should not happen.
1583 return;
1584 }
1585 uploadByteDevice->advanceReadPointer(amount);
1586 uploadByteDevicePosition += amount;
1587}
1588
1589// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1591{
1593
1594 if (!uploadByteDevice) // uploadByteDevice is no longer available
1595 return;
1596
1597 // call readPointer
1598 qint64 currentUploadDataLength = 0;
1599 char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
1600
1601 if (currentUploadDataLength == 0) {
1602 uploadDeviceChoking = true;
1603 // No bytes from upload byte device. There will be bytes later, it will emit readyRead()
1604 // and our uploadByteDeviceReadyReadSlot() is called.
1605 return;
1606 } else {
1607 uploadDeviceChoking = false;
1608 }
1609
1610 // Let's make a copy of this data
1611 QByteArray dataArray(data, currentUploadDataLength);
1612
1613 // Communicate back to HTTP thread
1614 emit q->haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
1615}
1616
1618{
1619 // Start the flow between this thread and the HTTP thread again by triggering a upload.
1620 // However only do this when we were choking before, else the state in
1621 // QNonContiguousByteDeviceThreadForwardImpl gets messed up.
1622 if (uploadDeviceChoking) {
1623 uploadDeviceChoking = false;
1624 wantUploadDataSlot(1024);
1625 }
1626}
1627
1628
1629/*
1630 A simple web page that can be used to test us: http://www.procata.com/cachetest/
1631 */
1633{
1635
1636 setCachingEnabled(false);
1637 if (!metaData.isValid())
1638 return false;
1639
1641 Q_ASSERT(nc);
1642 QIODevice *contents = nc->data(url);
1643 if (!contents) {
1644#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1645 qDebug() << "Cannot send cache, the contents are 0" << url;
1646#endif
1647 return false;
1648 }
1649 contents->setParent(q);
1650
1653 if (status < 100)
1654 status = 200; // fake it
1655
1656 statusCode = status;
1657
1658 q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
1661
1662 QHttpHeaders cachedHeaders = metaData.headers();
1664 QUrl redirectUrl;
1665 for (qsizetype i = 0; i < cachedHeaders.size(); ++i) {
1666 const auto name = cachedHeaders.nameAt(i);
1667 const auto value = cachedHeaders.valueAt(i);
1668
1670 && !name.compare(locationHeader(), Qt::CaseInsensitive)) {
1671 redirectUrl = QUrl::fromEncoded(value);
1672 }
1673
1674 h.replaceOrAppend(name, value);
1675 }
1676 setHeaders(std::move(h));
1677
1679 checkForRedirect(status);
1680
1682 q->connect(cacheLoadDevice, SIGNAL(readyRead()), SLOT(_q_cacheLoadReadyRead()));
1683 q->connect(cacheLoadDevice, SIGNAL(readChannelFinished()), SLOT(_q_cacheLoadReadyRead()));
1684
1685 // This needs to be emitted in the event loop because it can be reached at
1686 // the direct code path of qnam.get(...) before the user has a chance
1687 // to connect any signals.
1688 QMetaObject::invokeMethod(q, "_q_metaDataChanged", Qt::QueuedConnection);
1689 QMetaObject::invokeMethod(q, "_q_cacheLoadReadyRead", Qt::QueuedConnection);
1690
1691
1692#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1693 qDebug() << "Successfully sent cache:" << url << contents->size() << "bytes";
1694#endif
1695
1696 // Do redirect processing
1699 Q_ARG(QUrl, redirectUrl),
1700 Q_ARG(int, status),
1701 Q_ARG(int, httpRequest.redirectCount() - 1));
1702 }
1703
1704 // Set the following flag so we can ignore some signals from HTTP thread
1705 // that would still come
1706 loadingFromCache = true;
1707 return true;
1708}
1709
1711{
1712 return [value](QByteArrayView element)
1713 {
1714 return value.compare(element, Qt::CaseInsensitive) == 0;
1715 };
1716}
1717
1719{
1720 constexpr QByteArrayView headers[] = { "connection",
1721 "keep-alive",
1722 "proxy-authenticate",
1723 "proxy-authorization",
1724 "te",
1725 "trailers",
1726 "transfer-encoding",
1727 "upgrade"};
1728 return std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(header));
1729}
1730
1732{
1733 Q_Q(const QNetworkReplyHttpImpl);
1734
1735 QNetworkCacheMetaData metaData = oldMetaData;
1736 QHttpHeaders cacheHeaders = metaData.headers();
1737
1738 const auto newHeaders = q->headers();
1739 for (qsizetype i = 0; i < newHeaders.size(); ++i) {
1740 const auto name = newHeaders.nameAt(i);
1741 const auto value = newHeaders.valueAt(i);
1742
1743 if (isHopByHop(name))
1744 continue;
1745
1746 if (name.compare("set-cookie", Qt::CaseInsensitive) == 0)
1747 continue;
1748
1749 // for 4.6.0, we were planning to not store the date header in the
1750 // cached resource; through that we planned to reduce the number
1751 // of writes to disk when using a QNetworkDiskCache (i.e. don't
1752 // write to disk when only the date changes).
1753 // However, without the date we cannot calculate the age of the page
1754 // anymore.
1755 //if (header == "date")
1756 //continue;
1757
1758 // Don't store Warning 1xx headers
1759 if (name.compare("warning", Qt::CaseInsensitive) == 0) {
1760 if (value.size() == 3
1761 && value[0] == '1'
1762 && isAsciiDigit(value[1])
1763 && isAsciiDigit(value[2]))
1764 continue;
1765 }
1766
1767 if (cacheHeaders.contains(name)) {
1768 // Match the behavior of Firefox and assume Cache-Control: "no-transform"
1769 constexpr QByteArrayView headers[]=
1770 {"content-encoding", "content-range", "content-type"};
1771 if (std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(name)))
1772 continue;
1773 }
1774
1775 // IIS has been known to send "Content-Length: 0" on 304 responses, so
1776 // ignore this too
1777 if (statusCode == 304 && name.compare("content-length", Qt::CaseInsensitive) == 0)
1778 continue;
1779
1780#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1781 QByteArrayView n = newHeaders.value(name);
1782 QByteArrayView o = cacheHeaders.value(name);
1783 if (n != o && name.compare("date", Qt::CaseInsensitive) != 0) {
1784 qDebug() << "replacing" << name;
1785 qDebug() << "new" << n;
1786 qDebug() << "old" << o;
1787 }
1788#endif
1789 cacheHeaders.replaceOrAppend(name, value);
1790 }
1791 metaData.setHeaders(cacheHeaders);
1792
1793 bool checkExpired = true;
1794
1795 QHash<QByteArray, QByteArray> cacheControl;
1796 auto value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::CacheControl);
1797 if (!value.empty()) {
1798 cacheControl = parseHttpOptionHeader(value);
1799 QByteArray maxAge = cacheControl.value("max-age"_ba);
1800 if (!maxAge.isEmpty()) {
1801 checkExpired = false;
1803 dt = dt.addSecs(maxAge.toInt());
1804 metaData.setExpirationDate(dt);
1805 }
1806 }
1807 if (checkExpired) {
1808 if (const auto value = cacheHeaders.value(
1811 metaData.setExpirationDate(expiredDateTime);
1812 }
1813 }
1814
1815 if (const auto value = cacheHeaders.value(
1818 }
1819
1820
1821 bool canDiskCache;
1822 // only cache GET replies by default, all other replies (POST, PUT, DELETE)
1823 // are not cacheable by default (according to RFC 2616 section 9)
1825
1826 canDiskCache = true;
1827 // HTTP/1.1. Check the Cache-Control header
1828 if (cacheControl.contains("no-store"_ba))
1829 canDiskCache = false;
1830
1831 // responses to POST might be cacheable
1833
1834 canDiskCache = false;
1835 // some pages contain "expires:" and "cache-control: no-cache" field,
1836 // so we only might cache POST requests if we get "cache-control: max-age ..."
1837 if (cacheControl.contains("max-age"_ba))
1838 canDiskCache = true;
1839
1840 // responses to PUT and DELETE are not cacheable
1841 } else {
1842 canDiskCache = false;
1843 }
1844
1845 metaData.setSaveToDisk(canDiskCache);
1847 if (statusCode != 304) {
1848 // update the status code
1851 } else {
1852 // this is a redirection, keep the attributes intact
1853 attributes = oldMetaData.attributes();
1854 }
1855 metaData.setAttributes(attributes);
1856 return metaData;
1857}
1858
1860{
1861 Q_Q(const QNetworkReplyHttpImpl);
1862
1863 // Only GET operation supports resuming.
1865 return false;
1866
1867 const auto h = q->headers();
1868
1869 // Can only resume if server/resource supports Range header.
1870 const auto acceptRanges = h.value(QHttpHeaders::WellKnownHeader::AcceptRanges);
1871 if (acceptRanges.empty() || acceptRanges == "none")
1872 return false;
1873
1874 // We only support resuming for byte ranges.
1875 const auto range = h.value(QHttpHeaders::WellKnownHeader::Range);
1876 if (!range.empty()) {
1877 if (!range.startsWith(bytesEqualPrefix()))
1878 return false;
1879 }
1880
1881 // If we're using a download buffer then we don't support resuming/migration
1882 // right now. Too much trouble.
1884 return false;
1885
1886 return true;
1887}
1888
1893
1895{
1896 // Ensure this function is only being called once, and not at all if we were
1897 // cancelled
1898 if (state >= Working)
1899 return;
1900
1901 state = Working;
1902
1904
1906 if (synchronous) {
1907 state = Finished;
1908 q_func()->setFinished(true);
1909 }
1910}
1911
1913{
1915
1916 if (state != Working)
1917 return;
1919 return;
1920
1921 // FIXME Optimize to use zerocopy download buffer if it is a QBuffer.
1922 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
1923 // metaDataChanged ?
1924
1925 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
1927
1928 // emit readyRead before downloadProgress in case this will cause events to be
1929 // processed and we get into a recursive call (as in QProgressDialog).
1930
1931 if (!(isHttpRedirectResponse())) {
1932 // This readyRead() goes to the user. The user then may or may not read() anything.
1933 emit q->readyRead();
1934
1937 emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
1938 }
1939 }
1940
1941 // A signal we've emitted might be handled by a slot that aborts,
1942 // so we need to check for that and bail out if it's happened:
1943 if (!q->isOpen())
1944 return;
1945
1946 // If there are still bytes available in the cacheLoadDevice then the user did not read
1947 // in response to the readyRead() signal. This means we have to load from the cacheLoadDevice
1948 // and buffer that stuff. This is needed to be able to properly emit finished() later.
1950 buffer.append(cacheLoadDevice->readAll());
1951
1953 // check if end and we can read the EOF -1
1954 char c;
1955 qint64 actualCount = cacheLoadDevice->read(&c, 1);
1956 if (actualCount < 0) {
1958 cacheLoadDevice = nullptr;
1960 } else if (actualCount == 1) {
1961 // This is most probably not happening since most QIODevice returned something proper for bytesAvailable()
1962 // and had already been "emptied".
1964 }
1965 } else if ((!cacheLoadDevice->isSequential() && cacheLoadDevice->atEnd())) {
1966 // This codepath is in case the cache device is a QBuffer, e.g. from QNetworkDiskCache.
1968 cacheLoadDevice = nullptr;
1970 }
1971}
1972
1973
1975{
1977
1978 // make sure this is only called once, ever.
1979 //_q_bufferOutgoingData may call it or the readChannelFinished emission
1980 if (state != Buffering)
1981 return;
1982
1983 // disconnect signals
1986
1987 // finally, start the request
1988 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1989}
1990
1992{
1993 // do not keep a dangling pointer to the device around (device
1994 // is closing because e.g. QAbstractNetworkCache::remove() was called).
1995 cacheSaveDevice = nullptr;
1996}
1997
1999{
2001
2002 if (!outgoingDataBuffer) {
2003 // first call, create our buffer
2004 outgoingDataBuffer = std::make_shared<QRingBuffer>();
2005
2008 }
2009
2011 qint64 bytesToBuffer = 0;
2012
2013 // read data into our buffer
2014 forever {
2015 bytesToBuffer = outgoingData->bytesAvailable();
2016 // unknown? just try 2 kB, this also ensures we always try to read the EOF
2017 if (bytesToBuffer <= 0)
2018 bytesToBuffer = 2*1024;
2019
2020 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
2021 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
2022
2023 if (bytesBuffered == -1) {
2024 // EOF has been reached.
2025 outgoingDataBuffer->chop(bytesToBuffer);
2026
2028 break;
2029 } else if (bytesBuffered == 0) {
2030 // nothing read right now, just wait until we get called again
2031 outgoingDataBuffer->chop(bytesToBuffer);
2032
2033 break;
2034 } else {
2035 // don't break, try to read() again
2036 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
2037 }
2038 }
2039}
2040
2046
2048{
2050 if (!transferTimeout) {
2051 transferTimeout = new QTimer(q);
2055 }
2057 if (request.transferTimeoutAsDuration() > 0ms) {
2059 transferTimeout->setInterval(request.transferTimeoutAsDuration());
2062
2063 }
2064}
2065
2066// need to have this function since the reply is a private member variable
2067// and the special backends need to access this.
2069{
2071 if (isFinished)
2072 return;
2073
2075
2077 //choke signal emissions, except the first and last signals which are unconditional
2079 if (bytesSent != bytesTotal && uploadProgressSignalChoke.elapsed() < progressSignalInterval) {
2080 return;
2081 }
2083 } else {
2085 }
2086 }
2087 emit q->uploadProgress(bytesSent, bytesTotal);
2088}
2089
2091{
2093
2096 else if (outgoingData) {
2098 } else {
2099 return nullptr;
2100 }
2101
2102 // We want signal emissions only for normal asynchronous uploads
2103 if (!synchronous)
2106
2107 return uploadByteDevice.get();
2108}
2109
2111{
2112 // This gets called queued, just forward to real call then
2113 finished();
2114}
2115
2117{
2119 if (transferTimeout)
2121 if (state == Finished || state == Aborted)
2122 return;
2123
2124 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
2126 const qint64 totalSize = totalSizeOpt.value_or(-1);
2127
2128 // if we don't know the total size of or we received everything save the cache.
2129 // If the data is compressed then this is done in readData()
2130 if ((totalSize == -1 || bytesDownloaded == totalSize)
2131 && !decompressHelper.isValid()) {
2133 }
2134
2135 // We check for errorCode too as in case of SSL handshake failure, we still
2136 // get the HTTP redirect status code (301, 303 etc)
2138 return;
2139
2140 state = Finished;
2141 q->setFinished(true);
2142
2143 if (totalSize == -1) {
2144 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
2145 } else {
2146 emit q->downloadProgress(bytesDownloaded, totalSize);
2147 }
2148
2150 emit q->uploadProgress(0, 0);
2151
2152 emit q->readChannelFinished();
2153 emit q->finished();
2154}
2155
2160
2161
2163{
2165 // Can't set and emit multiple errors.
2167 // But somewhat unavoidable if we have cancelled the request:
2169 qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
2170 return;
2171 }
2172
2173 errorCode = code;
2174 q->setErrorString(errorMessage);
2175
2176 // note: might not be a good idea, since users could decide to delete us
2177 // which would delete the backend too...
2178 // maybe we should protect the backend
2179 emit q->errorOccurred(code);
2180}
2181
2183{
2184 // FIXME merge this with replyDownloadMetaData(); ?
2185
2187 // 1. do we have cookies?
2188 // 2. are we allowed to set them?
2190
2191 const auto cookiesOpt = QNetworkHeadersPrivate::toSetCookieList(
2193 const auto cookies = cookiesOpt.value_or(QList<QNetworkCookie>());
2194 if (!cookies.empty()
2198 if (jar) {
2199 jar->setCookiesFromUrl(cookies, url);
2200 }
2201 }
2202 emit q->metaDataChanged();
2203}
2204
2206{
2207 // check if we can save and if we're allowed to
2210 return;
2211 cacheEnabled = true;
2212}
2213
2218
2220{
2221 if (!enable && !cacheEnabled)
2222 return; // nothing to do
2223 if (enable && cacheEnabled)
2224 return; // nothing to do either!
2225
2226 if (enable) {
2228 qDebug() << "setCachingEnabled: " << bytesDownloaded << " bytesDownloaded";
2229 // refuse to enable in this case
2230 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
2231 return;
2232 }
2233
2234 createCache();
2235 } else {
2236 // someone told us to turn on, then back off?
2237 // ok... but you should make up your mind
2238 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false)");
2240 cacheSaveDevice = nullptr;
2241 cacheEnabled = false;
2242 }
2243}
2244
2249
2260
2262
2263#include "moc_qnetworkreplyhttpimpl_p.cpp"
The QAbstractNetworkCache class provides the interface for cache implementations.
virtual void insert(QIODevice *device)=0
Inserts the data in device and the prepared meta data into the cache.
virtual QIODevice * prepare(const QNetworkCacheMetaData &metaData)=0
Returns the device that should be populated with the data for the cache item metaData.
virtual bool remove(const QUrl &url)=0
Removes the cache entry for url, returning true if success otherwise false.
\inmodule QtCore
Definition qatomic.h:112
The QAuthenticator class provides an authentication object.
constexpr QByteArrayView mid(qsizetype pos, qsizetype n=-1) const
\inmodule QtCore
Definition qbytearray.h:57
int toInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an int using base base, which is ten by default.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
\inmodule QtCore\reentrant
Definition qdatetime.h:283
qint64 secsTo(const QDateTime &) const
Returns the number of seconds from this datetime to the other datetime.
QDateTime addSecs(qint64 secs) const
Returns a QDateTime object containing a datetime s seconds later than the datetime of this object (or...
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
static QDateTime currentDateTimeUtc()
qint64 daysTo(const QDateTime &) const
Returns the number of days from this datetime to the other datetime.
qint64 toSecsSinceEpoch() const
\inmodule QtCore
void setCountingBytesEnabled(bool shouldCount)
bool setEncoding(QByteArrayView contentEncoding)
void feed(const QByteArray &data)
qint64 uncompressedSize() const
qsizetype read(char *data, qsizetype maxSize)
void setDecompressedSafetyCheckThreshold(qint64 threshold)
QString errorString() const
void invalidate() noexcept
Marks this QElapsedTimer object as invalid.
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
bool isValid() const noexcept
Returns false if the timer has never been started or invalidated by a call to invalidate().
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
bool isKnownHost(const QUrl &url) const
Definition qhsts.cpp:134
void updateFromHeaders(const QHttpHeaders &headers, const QUrl &url)
Definition qhsts.cpp:45
Q_NETWORK_EXPORT bool contains(QAnyStringView name) const
Returns whether the headers contain header with name.
Q_NETWORK_EXPORT QLatin1StringView nameAt(qsizetype i) const noexcept
Returns the header name at index i.
Q_NETWORK_EXPORT QByteArrayView value(QAnyStringView name, QByteArrayView defaultValue={}) const noexcept
Returns the value of the (first) header name, or defaultValue if it doesn't exist.
static bool isHttpRedirect(int statusCode)
void setCustomVerb(const QByteArray &customOperation)
void setPriority(Priority priority)
void setPreConnect(bool preConnect)
void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
void setPeerVerifyName(const QString &peerName)
void setHeaderField(const QByteArray &name, const QByteArray &data) override
void setUrl(const QUrl &url) override
QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue=QByteArray()) const override
QUrl url() const override
void setOperation(Operation operation)
Operation operation() const
void setRedirectCount(int count)
QNetworkRequest::RedirectPolicy redirectPolicy() const
void setUploadByteDevice(QNonContiguousByteDevice *bd)
void socketStartedConnecting()
QScopedPointer< QSslConfiguration > incomingSslConfiguration
std::shared_ptr< QAtomicInt > pendingDownloadData
void downloadMetaData(const QHttpHeaders &, int, const QString &, bool, QSharedPointer< char >, qint64, qint64, bool, bool)
QHttpNetworkRequest httpRequest
QHttp1Configuration http1Parameters
QHttp2Configuration http2Parameters
std::shared_ptr< QAtomicInt > pendingDownloadProgress
QNetworkReply::NetworkError incomingErrorCode
std::shared_ptr< QNetworkAccessAuthenticationManager > authenticationManager
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
virtual bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
bool isOpen() const
Returns true if the device is open; otherwise returns false.
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
virtual bool reset()
Seeks to the start of input for random-access devices.
virtual bool atEnd() const
Returns true if the current read and write position is at the end of the device (i....
void ungetChar(char c)
Puts the character c back into the device, and decrements the current position unless the position is...
virtual bool canReadLine() const
Returns true if a complete line of data can be read from the device; otherwise returns false.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
bool isEmpty() const noexcept
Definition qlist.h:401
\inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection.
QList< QNetworkProxy > queryProxy(const QNetworkProxyQuery &query)
void proxyAuthenticationRequired(const QUrl &url, const QNetworkProxy &proxy, bool synchronous, QAuthenticator *authenticator, QNetworkProxy *lastProxyAuthentication)
void authenticationRequired(QAuthenticator *authenticator, QNetworkReply *reply, bool synchronous, QUrl &url, QUrl *urlForLastAuthentication, bool allowAuthenticationReuse=true)
std::shared_ptr< QNetworkAccessAuthenticationManager > authenticationManager
The QNetworkAccessManager class allows the application to send network requests and receive replies.
Operation
Indicates the operation this reply is processing.
QNetworkCookieJar * cookieJar() const
Returns the QNetworkCookieJar that is used to store cookies obtained from the network as well as cook...
The QNetworkCacheMetaData class provides cache information.
bool saveToDisk() const
Returns is this cache should be allowed to be stored on disk.
void setUrl(const QUrl &url)
Sets the URL this network cache meta data to be url.
void setSaveToDisk(bool allow)
Sets whether this network cache meta data and associated content should be allowed to be stored on di...
void setExpirationDate(const QDateTime &dateTime)
Sets the date and time when the meta data expires to dateTime.
bool isValid() const
Returns true if this network cache meta data has attributes that have been set otherwise false.
AttributesMap attributes() const
void setLastModified(const QDateTime &dateTime)
Sets the date and time when the meta data was last modified to dateTime.
QDateTime lastModified() const
Returns the date and time when the meta data was last modified.
QDateTime expirationDate() const
Returns the date and time when the meta data expires.
void setAttributes(const AttributesMap &attributes)
void setHeaders(const QHttpHeaders &headers)
The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects.
virtual bool setCookiesFromUrl(const QList< QNetworkCookie > &cookieList, const QUrl &url)
Adds the cookies in the list cookieList to this cookie jar.
static std::optional< qint64 > toInt(QByteArrayView value)
static QByteArray fromCookieList(const NetworkCookieList &cookies)
QHttpHeaders headers() const
static std::optional< NetworkCookieList > toSetCookieList(const QList< QByteArray > &values)
void setHeaders(const QHttpHeaders &newHeaders)
static QByteArray toHttpDate(const QDateTime &dt)
static QDateTime fromHttpDate(QByteArrayView value)
The QNetworkProxyQuery class is used to query the proxy settings for a socket.
The QNetworkProxy class provides a network layer proxy.
QNetworkProxy::ProxyType type() const
Returns the proxy type for this instance.
QHttpHeaders headers() const
QNetworkRequest createRedirectRequest(const QNetworkRequest &originalRequests, const QUrl &url, int maxRedirectsRemainig)
QSharedPointer< char > downloadBufferPointer
QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const
void _q_error(QNetworkReply::NetworkError code, const QString &errorString)
void httpError(QNetworkReply::NetworkError error, const QString &errorString)
std::shared_ptr< QAtomicInt > pendingDownloadProgressEmissions
QNetworkAccessManagerPrivate * managerPrivate
void replyDownloadMetaData(const QHttpHeaders &, int, const QString &, bool, QSharedPointer< char >, qint64, qint64, bool, bool)
static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority &prio)
void replySslConfigurationChanged(const QSslConfiguration &newSslConfiguration)
void onRedirected(const QUrl &redirectUrl, int httpStatus, int maxRedirectsRemainig)
void error(QNetworkReply::NetworkError code, const QString &errorString)
QNetworkAccessManager::Operation getRedirectOperation(QNetworkAccessManager::Operation currentOp, int httpStatus)
std::shared_ptr< QNonContiguousByteDevice > uploadByteDevice
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth)
void replyDownloadProgressSlot(qint64, qint64)
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth)
bool sendCacheContents(const QNetworkCacheMetaData &metaData)
void checkForRedirect(const int statusCode)
QScopedPointer< QSslConfiguration > sslConfiguration
QNonContiguousByteDevice * createUploadByteDevice()
std::shared_ptr< QAtomicInt > pendingDownloadDataEmissions
void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
void replySslErrors(const QList< QSslError > &, bool *, QList< QSslError > *)
bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest)
std::shared_ptr< QRingBuffer > outgoingDataBuffer
void replyPreSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *)
void postRequest(const QNetworkRequest &newHttpRequest)
qint64 readData(char *, qint64) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
void readBufferFreed(qint64 size)
void setSslConfigurationImplementation(const QSslConfiguration &configuration) override
void readBufferSizeChanged(qint64 size)
void abort() override
Aborts the operation immediately and close down any network connections still open.
void close() override
Closes this device for reading.
qint64 size() const override
For open random-access devices, this function returns the size of the device.
QNetworkReplyHttpImpl(QNetworkAccessManager *const, const QNetworkRequest &, QNetworkAccessManager::Operation &, QIODevice *outgoingData)
void void void void void void void void void void void void void void void void void void void void void void void void void followRedirect()) protected void ignoreSslErrorsImplementation(const QList< QSslError > &errors) override
void sslConfigurationImplementation(QSslConfiguration &configuration) const override
qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
bool isSequential() const override
bool canReadLine() const override
Returns true if a complete line of data can be read from the device; otherwise returns false.
void setReadBufferSize(qint64 size) override
Sets the size of the read buffer to be size bytes.
QElapsedTimer downloadProgressSignalChoke
QNetworkRequest request
static const int progressSignalInterval
QNetworkAccessManager::Operation operation
QNetworkRequest originalRequest
QElapsedTimer uploadProgressSignalChoke
QNetworkReply::NetworkError errorCode
The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager.
void socketStartedConnecting()
virtual void setReadBufferSize(qint64 size)
Sets the size of the read buffer to be size bytes.
void redirectAllowed()
QNetworkAccessManager * manager() const
Returns the QNetworkAccessManager that was used to create this QNetworkReply object.
void requestSent()
bool isFinished() const
virtual void close() override
Closes this device for reading.
QNetworkAccessManager::Operation operation() const
Returns the operation that was posted for this reply.
qint64 readBufferSize() const
Returns the size of the read buffer, in bytes.
NetworkError
Indicates all possible error conditions found during the processing of the request.
virtual void ignoreSslErrors()
If this function is called, SSL errors related to network connection will be ignored,...
QNetworkRequest request() const
Returns the request that was posted for this reply.
QUrl url() const
Returns the URL of the content downloaded or uploaded.
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
void setHeaders(const QHttpHeaders &newHeaders)
This is an overloaded member function, provided for convenience. It differs from the above function o...
@ EmitAllUploadProgressSignalsAttribute
@ ConnectionCacheExpiryTimeoutSecondsAttribute
QVariant attribute(Attribute code, const QVariant &defaultValue=QVariant()) const
Returns the attribute associated with the code code.
QHttpHeaders headers() const
void setMaximumRedirectsAllowed(int maximumRedirectsAllowed)
void setUrl(const QUrl &url)
Sets the URL this network request is referring to be url.
QUrl url() const
Returns the URL this network request is referring to.
CacheLoadControl
Controls the caching mechanism of QNetworkAccessManager.
QSslConfiguration sslConfiguration() const
Returns this network request's SSL configuration.
static std::shared_ptr< QNonContiguousByteDevice > createShared(QIODevice *device)
Create a QNonContiguousByteDevice out of a QIODevice, return it in a std::shared_ptr.
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
bool moveToThread(QThread *thread QT6_DECL_NEW_OVERLOAD_TAIL)
Changes the thread affinity for this object and its children and returns true on success.
Definition qobject.cpp:1643
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:127
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
T * data() const noexcept
Returns the value of the pointer referenced by this object.
The QSslConfiguration class holds the configuration and state of an SSL connection.
The QSslPreSharedKeyAuthenticator class provides authentication data for pre shared keys (PSK) cipher...
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
void start(Priority=InheritPriority)
Definition qthread.cpp:996
bool isFinished() const
Definition qthread.cpp:1059
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:1023
void quit()
Definition qthread.cpp:1008
\inmodule QtCore
Definition qtimer.h:20
void setSingleShot(bool singleShot)
Definition qtimer.cpp:552
void setInterval(int msec)
Definition qtimer.cpp:579
void stop()
Stops the timer.
Definition qtimer.cpp:267
\inmodule QtCore
Definition qurl.h:94
QString url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2817
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1882
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1991
void setScheme(const QString &scheme)
Sets the scheme of the URL to scheme.
Definition qurl.cpp:1967
int port(int defaultPort=-1) const
Definition qurl.cpp:2383
static QUrl fromEncoded(QByteArrayView input, ParsingMode mode=TolerantMode)
Parses input and returns the corresponding QUrl.
Definition qurl.cpp:2988
void setPort(int port)
Sets the port of the URL to port.
Definition qurl.cpp:2358
\inmodule QtCore
Definition qvariant.h:65
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
else opt state
[0]
Combined button and popup list for selecting options.
constexpr bool isAsciiDigit(char32_t c) noexcept
Definition qtools_p.h:67
@ CaseInsensitive
@ BlockingQueuedConnection
@ QueuedConnection
@ DirectConnection
#define Q_UNLIKELY(x)
DBusConnection const char DBusError * error
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define forever
Definition qforeach.h:78
static bool isLWS(int c)
Definition qhsts.cpp:258
static bool isSeparator(char c)
Definition qhsts.cpp:280
#define qCritical
Definition qlogging.h:167
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static int nextNonWhitespace(QByteArrayView text, int from)
static QByteArray cacheControlName()
static constexpr QLatin1StringView locationHeader() noexcept
static auto caseInsensitiveCompare(QByteArrayView value)
static QByteArray rangeName()
static bool isHopByHop(QByteArrayView header)
static constexpr QByteArrayView bytesEqualPrefix() noexcept
static QHash< QByteArray, QByteArray > parseHttpOptionHeader(QByteArrayView header)
#define SLOT(a)
Definition qobjectdefs.h:52
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLenum GLsizei GLsizei GLint * values
[15]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLbitfield GLuint64 timeout
[4]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLsizei range
GLenum GLuint buffer
GLenum GLenum dst
GLboolean enable
GLenum GLuint GLintptr offset
GLuint name
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
#define QStringLiteral(str)
#define tr(X)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
static bool ignoreAll
#define emit
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3517
static bool equal(const QChar *a, int l, const char *b)
Definition qurlidna.cpp:338
QList< int > list
[14]
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QMimeDatabase db
[0]
QNetworkAccessManager manager
QNetworkRequest request(url)
QNetworkProxy proxy
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...