Qt 6.x
The Qt SDK
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;
37
38class QNetworkProxy;
39
40// ### merge with nextField in cookiejar.cpp
42{
43 // The HTTP header is of the form:
44 // header = #1(directives)
45 // directives = token | value-directive
46 // value-directive = token "=" (token | quoted-string)
48
49 int pos = 0;
50 while (true) {
51 // skip spaces
53 if (pos == header.size())
54 return result; // end of parsing
55
56 // pos points to a non-whitespace
57 int comma = header.indexOf(',', pos);
58 int equal = header.indexOf('=', pos);
59 if (comma == pos || equal == pos)
60 // huh? Broken header.
61 return result;
62
63 // The key name is delimited by either a comma, an equal sign or the end
64 // of the header, whichever comes first
65 int end = comma;
66 if (end == -1)
67 end = header.size();
68 if (equal != -1 && end > equal)
69 end = equal; // equal sign comes before comma/end
71 pos = end + 1;
72
73 if (uint(equal) < uint(comma)) {
74 // case: token "=" (token | quoted-string)
75 // skip spaces
77 if (pos == header.size())
78 // huh? Broken header
79 return result;
80
82 value.reserve(header.size() - pos);
83 if (header.at(pos) == '"') {
84 // case: quoted-string
85 // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
86 // qdtext = <any TEXT except <">>
87 // quoted-pair = "\" CHAR
88 ++pos;
89 while (pos < header.size()) {
90 char c = header.at(pos);
91 if (c == '"') {
92 // end of quoted text
93 break;
94 } else if (c == '\\') {
95 ++pos;
96 if (pos >= header.size())
97 // broken header
98 return result;
99 c = header.at(pos);
100 }
101
102 value += c;
103 ++pos;
104 }
105 } else {
106 const auto isSeparator = [](char c) {
107 static const char separators[] = "()<>@,;:\\\"/[]?={}";
108 return isLWS(c) || strchr(separators, c) != nullptr;
109 };
110
111 // case: token
112 while (pos < header.size()) {
113 char c = header.at(pos);
114 if (isSeparator(c))
115 break;
116 value += c;
117 ++pos;
118 }
119 }
120
121 result.insert(key, value);
122
123 // find the comma now:
124 comma = header.indexOf(',', pos);
125 if (comma == -1)
126 return result; // end of parsing
127 pos = comma + 1;
128 } else {
129 // case: token
130 // key is already set
131 result.insert(key, QByteArray());
132 }
133 }
134}
135
139 QIODevice* outgoingData)
141{
144 d->manager = manager;
145 d->managerPrivate = manager->d_func();
146 d->request = request;
147 d->originalRequest = request;
148 d->operation = operation;
149 d->outgoingData = outgoingData;
150 d->url = request.url();
151#ifndef QT_NO_SSL
152 if (request.url().scheme() == "https"_L1)
153 d->sslConfiguration.reset(new QSslConfiguration(request.sslConfiguration()));
154#endif
155
158
159 // FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
161
162
163 // Internal code that does a HTTP reply for the synchronous Ajax
164 // in Qt WebKit.
165 QVariant synchronousHttpAttribute = request.attribute(
167 if (synchronousHttpAttribute.isValid()) {
168 d->synchronous = synchronousHttpAttribute.toBool();
169 if (d->synchronous && outgoingData) {
170 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
171 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
172 d->outgoingDataBuffer = std::make_shared<QRingBuffer>();
173 qint64 previousDataSize = 0;
174 do {
175 previousDataSize = d->outgoingDataBuffer->size();
176 d->outgoingDataBuffer->append(d->outgoingData->readAll());
177 } while (d->outgoingDataBuffer->size() != previousDataSize);
178 d->_q_startOperation();
179 return;
180 }
181 }
182
183
184 if (outgoingData) {
185 // there is data to be uploaded, e.g. HTTP POST.
186
187 if (!d->outgoingData->isSequential()) {
188 // fixed size non-sequential (random-access)
189 // just start the operation
190 QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
191 // FIXME make direct call?
192 } else {
193 bool bufferingDisallowed =
195 false).toBool();
196
197 if (bufferingDisallowed) {
198 // if a valid content-length header for the request was supplied, we can disable buffering
199 // if not, we will buffer anyway
201 QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
202 // FIXME make direct call?
203 } else {
204 d->state = d->Buffering;
205 QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
206 }
207 } else {
208 // _q_startOperation will be called when the buffering has finished.
209 d->state = d->Buffering;
210 QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
211 }
212 }
213 } else {
214 // No outgoing data (POST, ..)
215 d->_q_startOperation();
216 }
217}
218
220{
221 // This will do nothing if the request was already finished or aborted
223}
224
226{
228
229 if (d->state == QNetworkReplyPrivate::Aborted ||
231 return;
232
233 // According to the documentation close only stops the download
234 // by closing we can ignore the download part and continue uploading.
236
237 // call finished which will emit signals
238 // FIXME shouldn't this be emitted Queued?
239 d->error(OperationCanceledError, tr("Operation canceled"));
240 d->finished();
241}
242
244{
246 // FIXME
248 return;
249
251
252 if (d->state != QNetworkReplyPrivate::Finished) {
253 // call finished which will emit signals
254 // FIXME shouldn't this be emitted Queued?
255 d->error(OperationCanceledError, tr("Operation canceled"));
256 d->finished();
257 }
258
260
262}
263
265{
266 Q_D(const QNetworkReplyHttpImpl);
267
268 // if we load from cache device
269 if (d->cacheLoadDevice) {
270 return QNetworkReply::bytesAvailable() + d->cacheLoadDevice->bytesAvailable();
271 }
272
273 // zerocopy buffer
274 if (d->downloadZerocopyBuffer) {
275 return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
276 }
277
278 if (d->decompressHelper.isValid()) {
279 if (d->decompressHelper.isCountingBytes())
280 return QNetworkReply::bytesAvailable() + d->decompressHelper.uncompressedSize();
281 if (d->decompressHelper.hasData())
283 }
284
285 // normal buffer
287}
288
290{
291 // FIXME In the cache of a cached load or the zero-copy buffer we could actually be non-sequential.
292 // FIXME however this requires us to implement stuff like seek() too.
293 return true;
294}
295
297{
298 // FIXME At some point, this could return a proper value, e.g. if we're non-sequential.
299 return QNetworkReply::size();
300}
301
303{
305
306 // cacheload device
307 if (d->cacheLoadDevice) {
308 // FIXME bytesdownloaded, position etc?
309
310 qint64 ret = d->cacheLoadDevice->read(data, maxlen);
311 return ret;
312 }
313
314 // zerocopy buffer
315 if (d->downloadZerocopyBuffer) {
316 // FIXME bytesdownloaded, position etc?
317
318 qint64 howMuch = qMin(maxlen, (d->downloadBufferCurrentSize - d->downloadBufferReadPosition));
319 memcpy(data, d->downloadZerocopyBuffer + d->downloadBufferReadPosition, howMuch);
320 d->downloadBufferReadPosition += howMuch;
321 return howMuch;
322
323 }
324
325 if (d->decompressHelper.isValid() && (d->decompressHelper.hasData() || !isFinished())) {
326 if (maxlen == 0 || !d->decompressHelper.hasData())
327 return 0;
328 const qint64 bytesRead = d->decompressHelper.read(data, maxlen);
329 if (!d->decompressHelper.isValid()) {
331 QCoreApplication::translate("QHttp", "Decompression failed: %1")
332 .arg(d->decompressHelper.errorString()));
333 d->decompressHelper.clear();
334 return -1;
335 }
336 if (d->cacheSaveDevice) {
337 // Need to write to the cache now that we have the data
338 d->cacheSaveDevice->write(data, bytesRead);
339 // ... and if we've read everything then the cache can be closed.
340 if (isFinished() && !d->decompressHelper.hasData())
341 d->completeCacheSave();
342 }
343 // In case of buffer size restriction we need to emit that it has been emptied
344 qint64 wasBuffered = d->bytesBuffered;
345 d->bytesBuffered = 0;
346 if (readBufferSize())
347 emit readBufferFreed(wasBuffered);
348 return bytesRead;
349 }
350
351 // normal buffer
352 if (d->state == d->Finished || d->state == d->Aborted)
353 return -1;
354
355 qint64 wasBuffered = d->bytesBuffered;
356 d->bytesBuffered = 0;
357 if (readBufferSize())
358 emit readBufferFreed(wasBuffered);
359 return 0;
360}
361
363{
366 return;
367}
368
370{
371 Q_D(const QNetworkReplyHttpImpl);
372
374 return true;
375
376 if (d->cacheLoadDevice)
377 return d->cacheLoadDevice->canReadLine();
378
379 if (d->downloadZerocopyBuffer)
380 return memchr(d->downloadZerocopyBuffer + d->downloadBufferReadPosition, '\n', d->downloadBufferCurrentSize - d->downloadBufferReadPosition);
381
382 return false;
383}
384
385#ifndef QT_NO_SSL
387{
389 Q_ASSERT(d->managerPrivate);
390
391 if (d->managerPrivate->stsEnabled && d->managerPrivate->stsCache.isKnownHost(url())) {
392 // We cannot ignore any Security Transport-related errors for this host.
393 return;
394 }
395
396 d->pendingIgnoreAllSslErrors = true;
397}
398
400{
402 Q_ASSERT(d->managerPrivate);
403
404 if (d->managerPrivate->stsEnabled && d->managerPrivate->stsCache.isKnownHost(url())) {
405 // We cannot ignore any Security Transport-related errors for this host.
406 return;
407 }
408
409 // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
410 // is called before QNetworkAccessManager::get() (or post(), etc.)
411 d->pendingIgnoreSslErrorsList = errors;
412}
413
415{
416 // Setting a SSL configuration on a reply is not supported. The user needs to set
417 // her/his QSslConfiguration on the QNetworkRequest.
418 Q_UNUSED(newconfig);
419}
420
422{
423 Q_D(const QNetworkReplyHttpImpl);
424 if (d->sslConfiguration.data())
425 configuration = *d->sslConfiguration;
426 else
427 configuration = request().sslConfiguration();
428}
429#endif
430
434 , managerPrivate(nullptr)
435 , synchronous(false)
436 , state(Idle)
437 , statusCode(0)
438 , uploadByteDevicePosition(false)
439 , uploadDeviceChoking(false)
440 , outgoingData(nullptr)
441 , bytesUploaded(-1)
442 , cacheLoadDevice(nullptr)
443 , loadingFromCache(false)
444 , cacheSaveDevice(nullptr)
445 , cacheEnabled(false)
446 , resumeOffset(0)
447 , bytesDownloaded(0)
448 , bytesBuffered(0)
449 , transferTimeout(nullptr)
450 , downloadBufferReadPosition(0)
451 , downloadBufferCurrentSize(0)
452 , downloadZerocopyBuffer(nullptr)
453 , pendingDownloadDataEmissions(std::make_shared<QAtomicInt>())
454 , pendingDownloadProgressEmissions(std::make_shared<QAtomicInt>())
455 #ifndef QT_NO_SSL
456 , pendingIgnoreAllSslErrors(false)
457 #endif
458
459{
460}
461
463{
464}
465
466/*
467 For a given httpRequest
468 1) If AlwaysNetwork, return
469 2) If we have a cache entry for this url populate headers so the server can return 304
470 3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true
471 */
473{
474 QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
476 if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
477 // If the request does not already specify preferred cache-control
478 // force reload from the network and tell any caching proxy servers to reload too
479 if (!request.rawHeaderList().contains("Cache-Control")) {
480 httpRequest.setHeaderField("Cache-Control", "no-cache");
481 httpRequest.setHeaderField("Pragma", "no-cache");
482 }
483 return false;
484 }
485
486 // The disk cache API does not currently support partial content retrieval.
487 // That is why we don't use the disk cache for any such requests.
488 if (request.hasRawHeader("Range"))
489 return false;
490
492 if (!nc)
493 return false; // no local cache
494
496 if (!metaData.isValid())
497 return false; // not in cache
498
499 if (!metaData.saveToDisk())
500 return false;
501
502 QNetworkHeadersPrivate cacheHeaders;
504 cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
505
506 it = cacheHeaders.findRawHeader("content-length");
507 if (it != cacheHeaders.rawHeaders.constEnd()) {
508 if (nc->data(httpRequest.url())->size() < it->second.toLongLong())
509 return false; // The data is smaller than the content-length specified
510 }
511
512 it = cacheHeaders.findRawHeader("etag");
513 if (it != cacheHeaders.rawHeaders.constEnd())
514 httpRequest.setHeaderField("If-None-Match", it->second);
515
516 QDateTime lastModified = metaData.lastModified();
517 if (lastModified.isValid())
518 httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
519
520 it = cacheHeaders.findRawHeader("Cache-Control");
521 if (it != cacheHeaders.rawHeaders.constEnd()) {
523 if (cacheControl.contains("must-revalidate"))
524 return false;
525 if (cacheControl.contains("no-cache"))
526 return false;
527 }
528
529 QDateTime currentDateTime = QDateTime::currentDateTimeUtc();
530 QDateTime expirationDate = metaData.expirationDate();
531
532 bool response_is_fresh;
533 if (!expirationDate.isValid()) {
534 /*
535 * age_value
536 * is the value of Age: header received by the cache with
537 * this response.
538 * date_value
539 * is the value of the origin server's Date: header
540 * request_time
541 * is the (local) time when the cache made the request
542 * that resulted in this cached response
543 * response_time
544 * is the (local) time when the cache received the
545 * response
546 * now
547 * is the current (local) time
548 */
549 qint64 age_value = 0;
550 it = cacheHeaders.findRawHeader("age");
551 if (it != cacheHeaders.rawHeaders.constEnd())
552 age_value = it->second.toLongLong();
553
554 QDateTime dateHeader;
555 qint64 date_value = 0;
556 it = cacheHeaders.findRawHeader("date");
557 if (it != cacheHeaders.rawHeaders.constEnd()) {
558 dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
559 date_value = dateHeader.toSecsSinceEpoch();
560 }
561
562 qint64 now = currentDateTime.toSecsSinceEpoch();
563 qint64 request_time = now;
564 qint64 response_time = now;
565
566 // Algorithm from RFC 2616 section 13.2.3
567 qint64 apparent_age = qMax<qint64>(0, response_time - date_value);
568 qint64 corrected_received_age = qMax(apparent_age, age_value);
569 qint64 response_delay = response_time - request_time;
570 qint64 corrected_initial_age = corrected_received_age + response_delay;
571 qint64 resident_time = now - response_time;
572 qint64 current_age = corrected_initial_age + resident_time;
573
574 qint64 freshness_lifetime = 0;
575
576 // RFC 2616 13.2.4 Expiration Calculations
577 if (lastModified.isValid() && dateHeader.isValid()) {
578 qint64 diff = lastModified.secsTo(dateHeader);
579 freshness_lifetime = diff / 10;
580 if (httpRequest.headerField("Warning").isEmpty()) {
581 QDateTime dt = currentDateTime.addSecs(current_age);
582 if (currentDateTime.daysTo(dt) > 1)
583 httpRequest.setHeaderField("Warning", "113");
584 }
585 }
586
587 // the cache-saving code below sets the freshness_lifetime with (dateHeader - last_modified) / 10
588 // if "last-modified" is present, or to Expires otherwise
589 response_is_fresh = (freshness_lifetime > current_age);
590 } else {
591 // expiration date was calculated earlier (e.g. when storing object to the cache)
592 response_is_fresh = currentDateTime.secsTo(expirationDate) >= 0;
593 }
594
595 if (!response_is_fresh)
596 return false;
597
598#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
599 qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
600#endif
601 return sendCacheContents(metaData);
602}
603
605{
606 switch (prio) {
612 default:
614 }
615}
616
618{
620
621 QThread *thread = nullptr;
622 if (synchronous) {
623 // A synchronous HTTP request uses its own thread
624 thread = new QThread();
625 thread->setObjectName(QStringLiteral("Qt HTTP synchronous thread"));
626 QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
627 thread->start();
628 } else {
629 // We use the manager-global thread.
630 // At some point we could switch to having multiple threads if it makes sense.
631 thread = managerPrivate->createThread();
632 }
633
634 QUrl url = newHttpRequest.url();
637
638 QString scheme = url.scheme();
639 bool ssl = (scheme == "https"_L1 || scheme == "preconnect-https"_L1);
641 httpRequest.setSsl(ssl);
642
643 bool preConnect = (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1);
644 httpRequest.setPreConnect(preConnect);
645
646#ifndef QT_NO_NETWORKPROXY
647 QNetworkProxy transparentProxy, cacheProxy;
648
649 // FIXME the proxy stuff should be done in the HTTP thread
650 const auto proxies = managerPrivate->queryProxy(QNetworkProxyQuery(newHttpRequest.url()));
651 for (const QNetworkProxy &p : proxies) {
652 // use the first proxy that works
653 // for non-encrypted connections, any transparent or HTTP proxy
654 // for encrypted, only transparent proxies
655 if (!ssl
656 && (p.capabilities() & QNetworkProxy::CachingCapability)
657 && (p.type() == QNetworkProxy::HttpProxy ||
659 cacheProxy = p;
660 transparentProxy = QNetworkProxy::NoProxy;
661 break;
662 }
663 if (p.isTransparentProxy()) {
664 transparentProxy = p;
665 cacheProxy = QNetworkProxy::NoProxy;
666 break;
667 }
668 }
669
670 // check if at least one of the proxies
671 if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
672 cacheProxy.type() == QNetworkProxy::DefaultProxy) {
673 // unsuitable proxies
676 Q_ARG(QString, QNetworkReplyHttpImpl::tr("No suitable proxy found")));
678 return;
679 }
680#endif
681
682 auto redirectPolicy = QNetworkRequest::NoLessSafeRedirectPolicy;
684 if (value.isValid())
685 redirectPolicy = qvariant_cast<QNetworkRequest::RedirectPolicy>(value);
686
687 httpRequest.setRedirectPolicy(redirectPolicy);
688
689 httpRequest.setPriority(convert(newHttpRequest.priority()));
690 loadingFromCache = false;
691
692 switch (operation) {
696 return; // no need to send the request! :)
697 break;
698
702 return; // no need to send the request! :)
703 break;
704
709 break;
710
715 break;
716
720 break;
721
723 invalidateCache(); // for safety reasons, we don't know what the operation does
726 httpRequest.setCustomVerb(newHttpRequest.attribute(
728 break;
729
730 default:
731 break; // can't happen
732 }
733
734 QList<QByteArray> headers = newHttpRequest.rawHeaderList();
735 if (resumeOffset != 0) {
736 const int rangeIndex = headers.indexOf("Range");
737 if (rangeIndex != -1) {
738 // Need to adjust resume offset for user specified range
739
740 headers.removeAt(rangeIndex);
741
742 // We've already verified that requestRange starts with "bytes=", see canResume.
743 QByteArray requestRange = newHttpRequest.rawHeader("Range").mid(6);
744
745 int index = requestRange.indexOf('-');
746
747 quint64 requestStartOffset = requestRange.left(index).toULongLong();
748 quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
749
750 // In case an end offset is not given it is skipped from the request range
751 requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +
752 '-' + (requestEndOffset ? QByteArray::number(requestEndOffset) : QByteArray());
753
754 httpRequest.setHeaderField("Range", requestRange);
755 } else {
756 httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
757 }
758 }
759
760 for (const QByteArray &header : std::as_const(headers))
762
765
767 allowed.isValid() && allowed.canConvert<bool>()) {
768 httpRequest.setHTTP2Allowed(allowed.value<bool>());
769 }
771 // ### Qt7: Stop checking the environment variable
772 if (h2cAttribute.toBool()
773 || (!h2cAttribute.isValid() && qEnvironmentVariableIsSet("QT_NETWORK_H2C_ALLOWED"))) {
775 }
776
778 // Intentionally mutually exclusive - cannot be both direct and 'allowed'
781 }
782
783 if (static_cast<QNetworkRequest::LoadControl>
787
790
792
793 // Create the HTTP thread delegate
795 // Propagate Http/2 settings:
796 delegate->http2Parameters = request.http2Configuration();
797 delegate->http1Parameters = request.http1Configuration();
798
801
802 // For the synchronous HTTP, this is the normal way the delegate gets deleted
803 // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
804 QMetaObject::Connection threadFinishedConnection =
805 QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
806
807 // QTBUG-88063: When 'delegate' is deleted the connection will be added to 'thread''s orphaned
808 // connections list. This orphaned list will be cleaned up next time 'thread' emits a signal,
809 // unfortunately that's the finished signal. It leads to a soft-leak so we do this to disconnect
810 // it on deletion so that it cleans up the orphan immediately.
811 QObject::connect(delegate, &QObject::destroyed, delegate, [threadFinishedConnection]() {
812 if (bool(threadFinishedConnection))
813 QObject::disconnect(threadFinishedConnection);
814 });
815
816 // Set the properties it needs
817 delegate->httpRequest = httpRequest;
818#ifndef QT_NO_NETWORKPROXY
819 delegate->cacheProxy = cacheProxy;
820 delegate->transparentProxy = transparentProxy;
821#endif
822 delegate->ssl = ssl;
823#ifndef QT_NO_SSL
824 if (ssl)
825 delegate->incomingSslConfiguration.reset(new QSslConfiguration(newHttpRequest.sslConfiguration()));
826#endif
827
828 // Do we use synchronous HTTP?
829 delegate->synchronous = synchronous;
830
831 // The authentication manager is used to avoid the BlockingQueuedConnection communication
832 // from HTTP thread to user thread in some cases.
834
835 if (!synchronous) {
836 // Tell our zerocopy policy to the delegate
837 QVariant downloadBufferMaximumSizeAttribute = newHttpRequest.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
838 if (downloadBufferMaximumSizeAttribute.isValid()) {
839 delegate->downloadBufferMaximumSize = downloadBufferMaximumSizeAttribute.toLongLong();
840 } else {
841 // If there is no MaximumDownloadBufferSizeAttribute set (which is for the majority
842 // of QNetworkRequest) then we can assume we'll do it anyway for small HTTP replies.
843 // This helps with performance and memory fragmentation.
844 delegate->downloadBufferMaximumSize = 128*1024;
845 }
846
847
848 // These atomic integers are used for signal compression
851
852 // Connect the signals of the delegate to us
853 QObject::connect(delegate, SIGNAL(downloadData(QByteArray)),
856 QObject::connect(delegate, SIGNAL(downloadFinished()),
857 q, SLOT(replyFinished()),
865 QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
871 QObject::connect(delegate, SIGNAL(redirected(QUrl,int,int)),
872 q, SLOT(onRedirected(QUrl,int,int)),
874
875#ifndef QT_NO_SSL
876 QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
879#endif
880 // Those need to report back, therefore BlockingQueuedConnection
881 QObject::connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
884#ifndef QT_NO_NETWORKPROXY
888#endif
889#ifndef QT_NO_SSL
890 QObject::connect(delegate, SIGNAL(encrypted()), q, SLOT(replyEncrypted()),
892 QObject::connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
895 QObject::connect(delegate, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
898#endif
899 // This signal we will use to start the request.
900 QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
901 QObject::connect(q, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
902
903 // To throttle the connection.
904 QObject::connect(q, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64)));
905 QObject::connect(q, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64)));
906
907 if (uploadByteDevice) {
908 QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =
910 forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()
911 delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
912
913 // If the device in the user thread claims it has more data, keep the flow to HTTP thread going
914 QObject::connect(uploadByteDevice.get(), SIGNAL(readyRead()),
917
918 // From user thread to http thread:
919 QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)),
920 forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection);
921 QObject::connect(uploadByteDevice.get(), SIGNAL(readyRead()),
922 forwardUploadDevice, SIGNAL(readyRead()),
924
925 // From http thread to user thread:
926 QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
928 QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64,qint64)),
930 QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
931 q, SLOT(resetUploadDataSlot(bool*)),
932 Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
933 }
934 } else if (synchronous) {
935 QObject::connect(q, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);
936
937 if (uploadByteDevice) {
938 // For the synchronous HTTP use case the use thread (this one here) is blocked
939 // so we cannot use the asynchronous upload architecture.
940 // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
941 // use the uploadByteDevice provided to us by the QNetworkReplyImpl.
942 // The code that is in start() makes sure it is safe to use from a thread
943 // since it only wraps a QRingBuffer
945 }
946 }
947
948
949 // Move the delegate to the http thread
950 delegate->moveToThread(thread);
951 // This call automatically moves the uploadDevice too for the asynchronous case.
952
953 // Prepare timers for progress notifications
956
957 // Send an signal to the delegate so it starts working in the other thread
958 if (synchronous) {
959 emit q->startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
960
962 (delegate->incomingHeaders,
963 delegate->incomingStatusCode,
964 delegate->incomingReasonPhrase,
965 delegate->isPipeliningUsed,
967 delegate->incomingContentLength,
968 delegate->removedContentLength,
969 delegate->isHttp2Used,
970 delegate->isCompressed);
972
974 httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
975
976 thread->quit();
977 thread->wait(QDeadlineTimer(5000));
978 if (thread->isFinished())
979 delete thread;
980 else
981 QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
982
983 finished();
984 } else {
985 emit q->startHttpRequest(); // Signal to the HTTP thread and go back to user.
986 }
987}
988
990{
992 if (nc)
993 nc->remove(httpRequest.url());
994}
995
997{
999
1000 // The disk cache does not support partial content, so don't even try to
1001 // save any such content into the cache.
1002 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
1003 cacheEnabled = false;
1004 return;
1005 }
1006
1007 // save the meta data
1008 QNetworkCacheMetaData metaData;
1009 metaData.setUrl(url);
1010 metaData = fetchCacheMetaData(metaData);
1011
1012 // save the redirect request also in the cache
1013 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
1014 if (redirectionTarget.isValid()) {
1017 metaData.setAttributes(attributes);
1018 }
1019
1021
1022 if (cacheSaveDevice)
1024
1027 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
1028 "class %s probably needs to be fixed",
1029 managerPrivate->networkCache->metaObject()->className());
1030
1032 cacheSaveDevice = nullptr;
1033 cacheEnabled = false;
1034 }
1035}
1036
1038{
1040
1041 // If we're closed just ignore this data
1042 if (!q->isOpen())
1043 return;
1044
1045 // cache this, we need it later and it's invalidated when dealing with compressed data
1046 auto dataSize = d.size();
1047
1050
1051 if (decompressHelper.isValid()) {
1052 qint64 uncompressedBefore = -1;
1054 uncompressedBefore = decompressHelper.uncompressedSize();
1055
1056 decompressHelper.feed(std::move(d));
1057
1058 if (!decompressHelper.isValid()) {
1060 QCoreApplication::translate("QHttp", "Decompression failed: %1")
1063 return;
1064 }
1065
1066 if (!isHttpRedirectResponse()) {
1068 bytesDownloaded += (decompressHelper.uncompressedSize() - uncompressedBefore);
1070 }
1071
1072 if (synchronous) {
1073 d = QByteArray();
1074 const qsizetype increments = 16 * 1024;
1075 qint64 bytesRead = 0;
1076 while (decompressHelper.hasData()) {
1077 quint64 nextSize = quint64(d.size()) + quint64(increments);
1078 if (nextSize > quint64(std::numeric_limits<QByteArray::size_type>::max())) {
1081 "Data downloaded is too large to store"));
1083 return;
1084 }
1085 d.resize(nextSize);
1086 bytesRead += decompressHelper.read(d.data() + bytesRead, increments);
1087 if (!decompressHelper.isValid()) {
1089 QCoreApplication::translate("QHttp", "Decompression failed: %1")
1092 return;
1093 }
1094 }
1095 d.resize(bytesRead);
1096 // we're synchronous so we're not calling this function again; reset the decompressHelper
1098 }
1099 }
1100
1101 // This is going to look a little strange. When downloading data while a
1102 // HTTP redirect is happening (and enabled), we write the redirect
1103 // response to the cache. However, we do not append it to our internal
1104 // buffer as that will contain the response data only for the final
1105 // response
1106 // Note: For compressed data this is done in readData()
1109 }
1110
1111 // if decompressHelper is valid then we have compressed data, and this is handled above
1113 buffer.append(d);
1116 }
1118
1119 int pendingSignals = pendingDownloadDataEmissions->fetchAndSubAcquire(1) - 1;
1120 if (pendingSignals > 0) {
1121 // Some more signal emissions to this slot are pending.
1122 // Instead of writing the downstream data, we wait
1123 // and do it in the next call we get
1124 // (signal comppression)
1125 return;
1126 }
1127
1129 return;
1130
1131 // This can occur when downloading compressed data as some of the data may be the content
1132 // encoding's header. Don't emit anything for this.
1135 emit q->readBufferFreed(dataSize);
1136 return;
1137 }
1139
1141
1142 emit q->readyRead();
1143 // emit readyRead before downloadProgress in case this will cause events to be
1144 // processed and we get into a recursive call (as in QProgressDialog).
1148 emit q->downloadProgress(bytesDownloaded,
1149 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
1150 }
1151}
1152
1154{
1155 // We are already loading from cache, we still however
1156 // got this signal because it was posted already
1157 if (loadingFromCache)
1158 return;
1159
1160 finished();
1161}
1162
1164{
1165 // HTTP status code can be used to decide if we can redirect with a GET
1166 // operation or not. See http://www.ietf.org/rfc/rfc2616.txt [Sec 10.3] for
1167 // more details
1168
1169 // We MUST keep using the verb that was used originally when being redirected with 307 or 308.
1170 if (httpStatus == 307 || httpStatus == 308)
1171 return currentOp;
1172
1173 switch (currentOp) {
1176 default:
1177 break;
1178 }
1179 // Use GET for everything else.
1181}
1182
1184{
1186}
1187
1189 const QUrl &url,
1190 int maxRedirectsRemaining)
1191{
1192 QNetworkRequest newRequest(originalRequest);
1193 newRequest.setUrl(url);
1194 newRequest.setMaximumRedirectsAllowed(maxRedirectsRemaining);
1195
1196 return newRequest;
1197}
1198
1199void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int httpStatus, int maxRedirectsRemaining)
1200{
1204
1205 if (isFinished)
1206 return;
1207
1208 const QString schemeBefore(url.scheme());
1209 if (httpRequest.isFollowRedirects()) // update the reply's url as it could've changed
1210 url = redirectUrl;
1211
1213 // RFC6797, 8.3:
1214 // The UA MUST replace the URI scheme with "https" [RFC2818],
1215 // and if the URI contains an explicit port component of "80",
1216 // then the UA MUST convert the port component to be "443", or
1217 // if the URI contains an explicit port component that is not
1218 // equal to "80", the port component value MUST be preserved;
1219 // otherwise, if the URI does not contain an explicit port
1220 // component, the UA MUST NOT add one.
1221 url.setScheme("https"_L1);
1222 if (url.port() == 80)
1223 url.setPort(443);
1224 }
1225
1226 const bool isLessSafe = schemeBefore == "https"_L1 && url.scheme() == "http"_L1;
1228 && isLessSafe) {
1230 QCoreApplication::translate("QHttp", "Insecure redirect"));
1231 return;
1232 }
1233
1234 redirectRequest = createRedirectRequest(originalRequest, url, maxRedirectsRemaining);
1236
1237 // Clear stale headers, the relevant ones get set again later
1241 // possibly changed from not-GET/HEAD to GET/HEAD, make sure to get rid of upload device
1242 uploadByteDevice.reset();
1244 if (outgoingData) {
1247 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q,
1249 }
1250 outgoingData = nullptr;
1251 outgoingDataBuffer.reset();
1252 // We need to explicitly unset these headers so they're not reapplied to the httpRequest
1255 }
1256
1257 if (const QNetworkCookieJar *const cookieJar = manager->cookieJar()) {
1258 auto cookies = cookieJar->cookiesForUrl(url);
1259 if (!cookies.empty()) {
1261 QVariant::fromValue(cookies));
1262 }
1263 }
1264
1267
1268 emit q->redirected(url);
1269}
1270
1272{
1275
1277 rawHeaders.clear();
1279
1282
1285}
1286
1288{
1290 switch (statusCode) {
1291 case 301: // Moved Permanently
1292 case 302: // Found
1293 case 303: // See Other
1294 case 307: // Temporary Redirect
1295 case 308: // Permanent Redirect
1296 // What do we do about the caching of the HTML note?
1297 // The response to a 303 MUST NOT be cached, while the response to
1298 // all of the others is cacheable if the headers indicate it to be
1299 QByteArray header = q->rawHeader("location");
1301 if (!url.isValid())
1304 }
1305}
1306
1308 int sc, const QString &rp, bool pu,
1310 qint64 contentLength,
1311 qint64 removedContentLength,
1312 bool h2Used, bool isCompressed)
1313{
1315 Q_UNUSED(contentLength);
1316
1317 statusCode = sc;
1318 reasonPhrase = rp;
1319
1320#ifndef QT_NO_SSL
1321 // We parse this header only if we're using secure transport:
1322 //
1323 // RFC6797, 8.1
1324 // If an HTTP response is received over insecure transport, the UA MUST
1325 // ignore any present STS header field(s).
1326 if (url.scheme() == "https"_L1 && managerPrivate->stsEnabled)
1328#endif
1329 // Download buffer
1330 if (!db.isNull()) {
1335 }
1336
1338 q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, h2Used);
1339
1340 // A user having manually defined which encodings they accept is, for
1341 // somwehat unknown (presumed legacy compatibility) reasons treated as
1342 // disabling our decompression:
1343 const bool autoDecompress = request.rawHeader("accept-encoding").isEmpty();
1344 const bool shouldDecompress = isCompressed && autoDecompress;
1345 // reconstruct the HTTP header
1346 QList<QPair<QByteArray, QByteArray> > headerMap = hm;
1347 QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
1348 end = headerMap.constEnd();
1349 for (; it != end; ++it) {
1350 QByteArray value = q->rawHeader(it->first);
1351
1352 // Reset any previous "location" header set in the reply. In case of
1353 // redirects, we don't want to 'append' multiple location header values,
1354 // rather we keep only the latest one
1355 if (it->first.toLower() == "location")
1356 value.clear();
1357
1358 if (shouldDecompress && !decompressHelper.isValid()
1359 && it->first.compare("content-encoding", Qt::CaseInsensitive) == 0) {
1360
1361 if (!synchronous) // with synchronous all the data is expected to be handled at once
1363
1364 if (!decompressHelper.setEncoding(it->second)) {
1366 QCoreApplication::translate("QHttp", "Failed to initialize decompression: %1")
1368 return;
1369 }
1371 request.decompressedSafetyCheckThreshold());
1372 }
1373
1374 if (!value.isEmpty()) {
1375 // Why are we appending values for headers which are already
1376 // present?
1377 if (it->first.compare("set-cookie", Qt::CaseInsensitive) == 0)
1378 value += '\n';
1379 else
1380 value += ", ";
1381 }
1382 value += it->second;
1383 q->setRawHeader(it->first, value);
1384 }
1385
1388 if (removedContentLength != -1)
1389 q->setAttribute(QNetworkRequest::OriginalContentLengthAttribute, removedContentLength);
1390
1391 // is it a redirection?
1394
1395 if (statusCode >= 500 && statusCode < 600) {
1397 if (nc) {
1399 QNetworkHeadersPrivate cacheHeaders;
1400 cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
1402 it = cacheHeaders.findRawHeader("Cache-Control");
1403 bool mustReValidate = false;
1404 if (it != cacheHeaders.rawHeaders.constEnd()) {
1406 if (cacheControl.contains("must-revalidate"))
1407 mustReValidate = true;
1408 }
1409 if (!mustReValidate && sendCacheContents(metaData))
1410 return;
1411 }
1412 }
1413
1414 if (statusCode == 304) {
1415#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1416 qDebug() << "Received a 304 from" << request.url();
1417#endif
1419 if (nc) {
1420 QNetworkCacheMetaData oldMetaData = nc->metaData(httpRequest.url());
1421 QNetworkCacheMetaData metaData = fetchCacheMetaData(oldMetaData);
1422 if (oldMetaData != metaData)
1423 nc->updateMetaData(metaData);
1424 if (sendCacheContents(metaData))
1425 return;
1426 }
1427 }
1428
1429
1430 if (statusCode != 304 && statusCode != 303) {
1431 if (!isCachingEnabled())
1432 setCachingEnabled(true);
1433 }
1434
1436}
1437
1439{
1441
1442 // If we're closed just ignore this data
1443 if (!q->isOpen())
1444 return;
1445
1446 // we can be sure here that there is a download buffer
1447
1448 int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1;
1449 if (pendingSignals > 0) {
1450 // Let's ignore this signal and look at the next one coming in
1451 // (signal comppression)
1452 return;
1453 }
1454
1455 if (!q->isOpen())
1456 return;
1457
1458 if (cacheEnabled && isCachingAllowed() && bytesReceived == bytesTotal) {
1459 // Write everything in one go if we use a download buffer. might be more performant.
1461 // need to check again if cache enabled and device exists
1464 // FIXME where is it closed?
1465 }
1466
1468 return;
1469
1470 bytesDownloaded = bytesReceived;
1472
1473 downloadBufferCurrentSize = bytesReceived;
1474
1475 // Only emit readyRead when actual data is there
1476 // emit readyRead before downloadProgress in case this will cause events to be
1477 // processed and we get into a recursive call (as in QProgressDialog).
1478 if (bytesDownloaded > 0)
1479 emit q->readyRead();
1482 emit q->downloadProgress(bytesDownloaded, bytesTotal);
1483 }
1484}
1485
1487 QAuthenticator *auth)
1488{
1490}
1491
1492#ifndef QT_NO_NETWORKPROXY
1494 QAuthenticator *authenticator)
1495{
1497}
1498#endif
1499
1501 const QString &errorString)
1502{
1503#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1504 qDebug() << "http error!" << errorCode << errorString;
1505#endif
1506
1507 // FIXME?
1509}
1510
1511#ifndef QT_NO_SSL
1513{
1515 emit q->encrypted();
1516}
1517
1519 const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored)
1520{
1522 emit q->sslErrors(list);
1523 // Check if the callback set any ignore and return this here to http thread
1525 *ignoreAll = true;
1527 *toBeIgnored = pendingIgnoreSslErrorsList;
1528}
1529
1531{
1532 // Receiving the used SSL configuration from the HTTP thread
1533 if (sslConfiguration.data())
1534 *sslConfiguration = newSslConfiguration;
1535 else
1536 sslConfiguration.reset(new QSslConfiguration(newSslConfiguration));
1537}
1538
1540{
1542 emit q->preSharedKeyAuthenticationRequired(authenticator);
1543}
1544#endif
1545
1546// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1548{
1549 *r = uploadByteDevice->reset();
1550 if (*r) {
1551 // reset our own position which is used for the inter-thread communication
1553 }
1554}
1555
1556// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1558{
1559 if (!uploadByteDevice) // uploadByteDevice is no longer available
1560 return;
1561
1562 if (uploadByteDevicePosition + amount != pos) {
1563 // Sanity check, should not happen.
1565 return;
1566 }
1567 uploadByteDevice->advanceReadPointer(amount);
1568 uploadByteDevicePosition += amount;
1569}
1570
1571// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1573{
1575
1576 if (!uploadByteDevice) // uploadByteDevice is no longer available
1577 return;
1578
1579 // call readPointer
1580 qint64 currentUploadDataLength = 0;
1581 char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
1582
1583 if (currentUploadDataLength == 0) {
1584 uploadDeviceChoking = true;
1585 // No bytes from upload byte device. There will be bytes later, it will emit readyRead()
1586 // and our uploadByteDeviceReadyReadSlot() is called.
1587 return;
1588 } else {
1589 uploadDeviceChoking = false;
1590 }
1591
1592 // Let's make a copy of this data
1593 QByteArray dataArray(data, currentUploadDataLength);
1594
1595 // Communicate back to HTTP thread
1596 emit q->haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
1597}
1598
1600{
1601 // Start the flow between this thread and the HTTP thread again by triggering a upload.
1602 // However only do this when we were choking before, else the state in
1603 // QNonContiguousByteDeviceThreadForwardImpl gets messed up.
1604 if (uploadDeviceChoking) {
1605 uploadDeviceChoking = false;
1606 wantUploadDataSlot(1024);
1607 }
1608}
1609
1610
1611/*
1612 A simple web page that can be used to test us: http://www.procata.com/cachetest/
1613 */
1615{
1617
1618 setCachingEnabled(false);
1619 if (!metaData.isValid())
1620 return false;
1621
1623 Q_ASSERT(nc);
1624 QIODevice *contents = nc->data(url);
1625 if (!contents) {
1626#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1627 qDebug() << "Cannot send cache, the contents are 0" << url;
1628#endif
1629 return false;
1630 }
1631 contents->setParent(q);
1632
1635 if (status < 100)
1636 status = 200; // fake it
1637
1638 statusCode = status;
1639
1640 q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
1643
1647 QUrl redirectUrl;
1648 for ( ; it != end; ++it) {
1650 !it->first.compare("location", Qt::CaseInsensitive))
1651 redirectUrl = QUrl::fromEncoded(it->second);
1652 setRawHeader(it->first, it->second);
1653 }
1654
1656 checkForRedirect(status);
1657
1659 q->connect(cacheLoadDevice, SIGNAL(readyRead()), SLOT(_q_cacheLoadReadyRead()));
1660 q->connect(cacheLoadDevice, SIGNAL(readChannelFinished()), SLOT(_q_cacheLoadReadyRead()));
1661
1662 // This needs to be emitted in the event loop because it can be reached at
1663 // the direct code path of qnam.get(...) before the user has a chance
1664 // to connect any signals.
1665 QMetaObject::invokeMethod(q, "_q_metaDataChanged", Qt::QueuedConnection);
1666 QMetaObject::invokeMethod(q, "_q_cacheLoadReadyRead", Qt::QueuedConnection);
1667
1668
1669#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1670 qDebug() << "Successfully sent cache:" << url << contents->size() << "bytes";
1671#endif
1672
1673 // Do redirect processing
1676 Q_ARG(QUrl, redirectUrl),
1677 Q_ARG(int, status),
1678 Q_ARG(int, httpRequest.redirectCount() - 1));
1679 }
1680
1681 // Set the following flag so we can ignore some signals from HTTP thread
1682 // that would still come
1683 loadingFromCache = true;
1684 return true;
1685}
1686
1688{
1689 Q_Q(const QNetworkReplyHttpImpl);
1690
1691 QNetworkCacheMetaData metaData = oldMetaData;
1692
1693 QNetworkHeadersPrivate cacheHeaders;
1694 cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
1696
1697 const QList<QByteArray> newHeaders = q->rawHeaderList();
1698 for (QByteArray header : newHeaders) {
1699 QByteArray originalHeader = header;
1700 header = header.toLower();
1701 bool hop_by_hop =
1702 (header == "connection"
1703 || header == "keep-alive"
1704 || header == "proxy-authenticate"
1705 || header == "proxy-authorization"
1706 || header == "te"
1707 || header == "trailers"
1708 || header == "transfer-encoding"
1709 || header == "upgrade");
1710 if (hop_by_hop)
1711 continue;
1712
1713 if (header == "set-cookie")
1714 continue;
1715
1716 // for 4.6.0, we were planning to not store the date header in the
1717 // cached resource; through that we planned to reduce the number
1718 // of writes to disk when using a QNetworkDiskCache (i.e. don't
1719 // write to disk when only the date changes).
1720 // However, without the date we cannot calculate the age of the page
1721 // anymore.
1722 //if (header == "date")
1723 //continue;
1724
1725 // Don't store Warning 1xx headers
1726 if (header == "warning") {
1727 QByteArray v = q->rawHeader(header);
1728 if (v.size() == 3
1729 && v[0] == '1'
1730 && isAsciiDigit(v[1])
1731 && isAsciiDigit(v[2]))
1732 continue;
1733 }
1734
1735 it = cacheHeaders.findRawHeader(header);
1736 if (it != cacheHeaders.rawHeaders.constEnd()) {
1737 // Match the behavior of Firefox and assume Cache-Control: "no-transform"
1738 if (header == "content-encoding"
1739 || header == "content-range"
1740 || header == "content-type")
1741 continue;
1742 }
1743
1744 // IIS has been known to send "Content-Length: 0" on 304 responses, so
1745 // ignore this too
1746 if (header == "content-length" && statusCode == 304)
1747 continue;
1748
1749#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1750 QByteArray n = q->rawHeader(header);
1751 QByteArray o;
1752 if (it != cacheHeaders.rawHeaders.constEnd())
1753 o = (*it).second;
1754 if (n != o && header != "date") {
1755 qDebug() << "replacing" << header;
1756 qDebug() << "new" << n;
1757 qDebug() << "old" << o;
1758 }
1759#endif
1760 cacheHeaders.setRawHeader(originalHeader, q->rawHeader(header));
1761 }
1762 metaData.setRawHeaders(cacheHeaders.rawHeaders);
1763
1764 bool checkExpired = true;
1765
1766 QHash<QByteArray, QByteArray> cacheControl;
1767 it = cacheHeaders.findRawHeader("Cache-Control");
1768 if (it != cacheHeaders.rawHeaders.constEnd()) {
1769 cacheControl = parseHttpOptionHeader(it->second);
1770 QByteArray maxAge = cacheControl.value("max-age");
1771 if (!maxAge.isEmpty()) {
1772 checkExpired = false;
1774 dt = dt.addSecs(maxAge.toInt());
1775 metaData.setExpirationDate(dt);
1776 }
1777 }
1778 if (checkExpired) {
1779 it = cacheHeaders.findRawHeader("expires");
1780 if (it != cacheHeaders.rawHeaders.constEnd()) {
1781 QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(it->second);
1782 metaData.setExpirationDate(expiredDateTime);
1783 }
1784 }
1785
1786 it = cacheHeaders.findRawHeader("last-modified");
1787 if (it != cacheHeaders.rawHeaders.constEnd())
1789
1790 bool canDiskCache;
1791 // only cache GET replies by default, all other replies (POST, PUT, DELETE)
1792 // are not cacheable by default (according to RFC 2616 section 9)
1794
1795 canDiskCache = true;
1796 // HTTP/1.1. Check the Cache-Control header
1797 if (cacheControl.contains("no-store"))
1798 canDiskCache = false;
1799
1800 // responses to POST might be cacheable
1802
1803 canDiskCache = false;
1804 // some pages contain "expires:" and "cache-control: no-cache" field,
1805 // so we only might cache POST requests if we get "cache-control: max-age ..."
1806 if (cacheControl.contains("max-age"))
1807 canDiskCache = true;
1808
1809 // responses to PUT and DELETE are not cacheable
1810 } else {
1811 canDiskCache = false;
1812 }
1813
1814 metaData.setSaveToDisk(canDiskCache);
1816 if (statusCode != 304) {
1817 // update the status code
1820 } else {
1821 // this is a redirection, keep the attributes intact
1822 attributes = oldMetaData.attributes();
1823 }
1824 metaData.setAttributes(attributes);
1825 return metaData;
1826}
1827
1829{
1830 Q_Q(const QNetworkReplyHttpImpl);
1831
1832 // Only GET operation supports resuming.
1834 return false;
1835
1836 // Can only resume if server/resource supports Range header.
1837 QByteArray acceptRangesheaderName("Accept-Ranges");
1838 if (!q->hasRawHeader(acceptRangesheaderName) || q->rawHeader(acceptRangesheaderName) == "none")
1839 return false;
1840
1841 // We only support resuming for byte ranges.
1842 if (request.hasRawHeader("Range")) {
1843 QByteArray range = request.rawHeader("Range");
1844 if (!range.startsWith("bytes="))
1845 return false;
1846 }
1847
1848 // If we're using a download buffer then we don't support resuming/migration
1849 // right now. Too much trouble.
1851 return false;
1852
1853 return true;
1854}
1855
1857{
1859}
1860
1862{
1863 if (state == Working) // ensure this function is only being called once
1864 return;
1865
1866 state = Working;
1867
1869
1871 if (synchronous) {
1872 state = Finished;
1873 q_func()->setFinished(true);
1874 }
1875}
1876
1878{
1880
1881 if (state != Working)
1882 return;
1884 return;
1885
1886 // FIXME Optimize to use zerocopy download buffer if it is a QBuffer.
1887 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
1888 // metaDataChanged ?
1889
1890
1892
1893 // emit readyRead before downloadProgress in case this will cause events to be
1894 // processed and we get into a recursive call (as in QProgressDialog).
1895
1896 if (!(isHttpRedirectResponse())) {
1897 // This readyRead() goes to the user. The user then may or may not read() anything.
1898 emit q->readyRead();
1899
1902 emit q->downloadProgress(bytesDownloaded,
1903 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
1904 }
1905 }
1906
1907 // A signal we've emitted might be handled by a slot that aborts,
1908 // so we need to check for that and bail out if it's happened:
1909 if (!q->isOpen())
1910 return;
1911
1912 // If there are still bytes available in the cacheLoadDevice then the user did not read
1913 // in response to the readyRead() signal. This means we have to load from the cacheLoadDevice
1914 // and buffer that stuff. This is needed to be able to properly emit finished() later.
1916 buffer.append(cacheLoadDevice->readAll());
1917
1919 // check if end and we can read the EOF -1
1920 char c;
1921 qint64 actualCount = cacheLoadDevice->read(&c, 1);
1922 if (actualCount < 0) {
1924 cacheLoadDevice = nullptr;
1926 } else if (actualCount == 1) {
1927 // This is most probably not happening since most QIODevice returned something proper for bytesAvailable()
1928 // and had already been "emptied".
1930 }
1931 } else if ((!cacheLoadDevice->isSequential() && cacheLoadDevice->atEnd())) {
1932 // This codepath is in case the cache device is a QBuffer, e.g. from QNetworkDiskCache.
1934 cacheLoadDevice = nullptr;
1936 }
1937}
1938
1939
1941{
1943
1944 // make sure this is only called once, ever.
1945 //_q_bufferOutgoingData may call it or the readChannelFinished emission
1946 if (state != Buffering)
1947 return;
1948
1949 // disconnect signals
1952
1953 // finally, start the request
1954 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1955}
1956
1958{
1959 // do not keep a dangling pointer to the device around (device
1960 // is closing because e.g. QAbstractNetworkCache::remove() was called).
1961 cacheSaveDevice = nullptr;
1962}
1963
1965{
1967
1968 if (!outgoingDataBuffer) {
1969 // first call, create our buffer
1970 outgoingDataBuffer = std::make_shared<QRingBuffer>();
1971
1974 }
1975
1977 qint64 bytesToBuffer = 0;
1978
1979 // read data into our buffer
1980 forever {
1981 bytesToBuffer = outgoingData->bytesAvailable();
1982 // unknown? just try 2 kB, this also ensures we always try to read the EOF
1983 if (bytesToBuffer <= 0)
1984 bytesToBuffer = 2*1024;
1985
1986 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
1987 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
1988
1989 if (bytesBuffered == -1) {
1990 // EOF has been reached.
1991 outgoingDataBuffer->chop(bytesToBuffer);
1992
1994 break;
1995 } else if (bytesBuffered == 0) {
1996 // nothing read right now, just wait until we get called again
1997 outgoingDataBuffer->chop(bytesToBuffer);
1998
1999 break;
2000 } else {
2001 // don't break, try to read() again
2002 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
2003 }
2004 }
2005}
2006
2008{
2010 q->abort();
2011}
2012
2014{
2016 if (!transferTimeout) {
2017 transferTimeout = new QTimer(q);
2021 }
2023 if (request.transferTimeout()) {
2025 transferTimeout->setInterval(request.transferTimeout());
2028
2029 }
2030}
2031
2032// need to have this function since the reply is a private member variable
2033// and the special backends need to access this.
2035{
2037 if (isFinished)
2038 return;
2039
2041
2043 //choke signal emissions, except the first and last signals which are unconditional
2045 if (bytesSent != bytesTotal && uploadProgressSignalChoke.elapsed() < progressSignalInterval) {
2046 return;
2047 }
2049 } else {
2051 }
2052 }
2053 emit q->uploadProgress(bytesSent, bytesTotal);
2054}
2055
2057{
2059
2062 else if (outgoingData) {
2064 } else {
2065 return nullptr;
2066 }
2067
2068 // We want signal emissions only for normal asynchronous uploads
2069 if (!synchronous)
2072
2073 return uploadByteDevice.get();
2074}
2075
2077{
2078 // This gets called queued, just forward to real call then
2079 finished();
2080}
2081
2083{
2085 if (transferTimeout)
2087 if (state == Finished || state == Aborted)
2088 return;
2089
2091
2092 // if we don't know the total size of or we received everything save the cache.
2093 // If the data is compressed then this is done in readData()
2094 if ((totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
2095 && !decompressHelper.isValid()) {
2097 }
2098
2099 // We check for errorCode too as in case of SSL handshake failure, we still
2100 // get the HTTP redirect status code (301, 303 etc)
2102 return;
2103
2104 state = Finished;
2105 q->setFinished(true);
2106
2107 if (totalSize.isNull() || totalSize == -1) {
2108 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
2109 } else {
2110 emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
2111 }
2112
2114 emit q->uploadProgress(0, 0);
2115
2116 emit q->readChannelFinished();
2117 emit q->finished();
2118}
2119
2121{
2122 this->error(code, errorMessage);
2123}
2124
2125
2127{
2129 // Can't set and emit multiple errors.
2131 qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
2132 return;
2133 }
2134
2135 errorCode = code;
2136 q->setErrorString(errorMessage);
2137
2138 // note: might not be a good idea, since users could decide to delete us
2139 // which would delete the backend too...
2140 // maybe we should protect the backend
2141 emit q->errorOccurred(code);
2142}
2143
2145{
2146 // FIXME merge this with replyDownloadMetaData(); ?
2147
2149 // 1. do we have cookies?
2150 // 2. are we allowed to set them?
2153 if (it != cookedHeaders.cend()
2157 if (jar) {
2158 QList<QNetworkCookie> cookies =
2159 qvariant_cast<QList<QNetworkCookie> >(it.value());
2160 jar->setCookiesFromUrl(cookies, url);
2161 }
2162 }
2163 emit q->metaDataChanged();
2164}
2165
2167{
2168 // check if we can save and if we're allowed to
2171 return;
2172 cacheEnabled = true;
2173}
2174
2176{
2177 return (cacheEnabled && managerPrivate->networkCache != nullptr);
2178}
2179
2181{
2182 if (!enable && !cacheEnabled)
2183 return; // nothing to do
2184 if (enable && cacheEnabled)
2185 return; // nothing to do either!
2186
2187 if (enable) {
2189 qDebug() << "setCachingEnabled: " << bytesDownloaded << " bytesDownloaded";
2190 // refuse to enable in this case
2191 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
2192 return;
2193 }
2194
2195 createCache();
2196 } else {
2197 // someone told us to turn on, then back off?
2198 // ok... but you should make up your mind
2199 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false)");
2201 cacheSaveDevice = nullptr;
2202 cacheEnabled = false;
2203 }
2204}
2205
2207{
2209}
2210
2212{
2215 } else if (cacheEnabled && cacheSaveDevice) {
2217 }
2218 cacheSaveDevice = nullptr;
2219 cacheEnabled = false;
2220}
2221
2223
2224#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 * data(const QUrl &url)=0
Returns the data associated with url.
virtual void updateMetaData(const QNetworkCacheMetaData &metaData)=0
Updates the cache meta date for the metaData's url to metaData.
virtual QNetworkCacheMetaData metaData(const QUrl &url)=0
Returns the meta data for the url url.
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.
\inmodule QtCore
Definition qbytearray.h:57
QByteArray trimmed() const &
Definition qbytearray.h:198
qulonglong toULongLong(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an {unsigned long long} using base base, which is ten by default.
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.
qsizetype indexOf(char c, qsizetype from=0) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void clear()
Clears the contents of the byte array and makes it null.
QByteArray toLower() const &
Definition qbytearray.h:190
QByteArray left(qsizetype len) const
Returns a byte array that contains the first len bytes of this byte array.
QByteArray mid(qsizetype index, qsizetype len=-1) const
Returns a byte array containing len bytes from this byte array, starting at position pos.
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
\inmodule QtCore\reentrant
Definition qdatetime.h:257
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)
void feed(const QByteArray &data)
qint64 uncompressedSize() const
bool setEncoding(const QByteArray &contentEncoding)
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
Starts this timer.
bool isValid() const noexcept
Returns false if the timer has never been started or invalidated by a call to invalidate().
\inmodule QtCore
Definition qhash.h:818
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
T value(const Key &key) const noexcept
Definition qhash.h:1044
const_iterator cend() const noexcept
Definition qhash.h:1208
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:949
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
bool isKnownHost(const QUrl &url) const
Definition qhsts.cpp:132
void updateFromHeaders(const QList< QPair< QByteArray, QByteArray > > &headers, const QUrl &url)
Definition qhsts.cpp:43
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
QUrl url() const override
void setOperation(Operation operation)
Operation operation() const
QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue=QByteArray()) const override
void setRedirectCount(int count)
QNetworkRequest::RedirectPolicy redirectPolicy() const
void setUploadByteDevice(QNonContiguousByteDevice *bd)
QList< QPair< QByteArray, QByteArray > > incomingHeaders
void socketStartedConnecting()
QScopedPointer< QSslConfiguration > incomingSslConfiguration
std::shared_ptr< QAtomicInt > pendingDownloadData
QHttpNetworkRequest httpRequest
void downloadMetaData(const QList< QPair< QByteArray, QByteArray > > &, int, const QString &, bool, QSharedPointer< char >, qint64, qint64, bool, bool)
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 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.
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
void removeAt(qsizetype i)
Definition qlist.h:573
const_iterator constBegin() const noexcept
Definition qlist.h:615
const_iterator constEnd() const noexcept
Definition qlist.h:616
void clear()
Definition qlist.h:417
const_iterator ConstIterator
Definition qlist.h:251
\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 setRawHeaders(const RawHeaderList &headers)
Sets the raw headers to list.
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
RawHeaderList rawHeaders() const
Returns a list of all raw headers that are set in this meta data.
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)
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.
void setAllRawHeaders(const RawHeadersList &list)
void setRawHeader(const QByteArray &key, const QByteArray &value)
static QByteArray toHttpDate(const QDateTime &dt)
RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const
CookedHeadersMap cookedHeaders
static QDateTime fromHttpDate(const QByteArray &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.
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
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)
void replyDownloadMetaData(const QList< QPair< QByteArray, QByteArray > > &, int, const QString &, bool, QSharedPointer< char >, qint64, qint64, bool, bool)
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.
@ EmitAllUploadProgressSignalsAttribute
@ ConnectionCacheExpiryTimeoutSecondsAttribute
void setHeader(KnownHeaders header, const QVariant &value)
Sets the value of the known header header to be value, overriding any previously set headers.
Priority priority() const
QVariant attribute(Attribute code, const QVariant &defaultValue=QVariant()) const
Returns the attribute associated with the code code.
QVariant header(KnownHeaders header) const
Returns the value of the known network header header if it is present in this request.
int maximumRedirectsAllowed() const
QList< QByteArray > rawHeaderList() const
Returns a list of all raw headers that are set in this network request.
bool hasRawHeader(const QByteArray &headerName) const
Returns true if the raw header headerName is present in this network request.
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.
QString peerVerifyName() const
QByteArray rawHeader(const QByteArray &headerName) const
Returns the raw form of header headerName.
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:298
void moveToThread(QThread *thread)
Changes the thread affinity for this object and its children.
Definition qobject.cpp:1606
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2142
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:114
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:2352
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.
\inmodule QtCore
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:127
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
QString toLower() const &
Definition qstring.h:368
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
void start(Priority=InheritPriority)
Definition qthread.cpp:923
bool isFinished() const
Definition qthread.cpp:981
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:950
void quit()
Definition qthread.cpp:935
\inmodule QtCore
Definition qtimer.h:20
void setSingleShot(bool singleShot)
Definition qtimer.cpp:580
void setInterval(int msec)
Definition qtimer.cpp:607
void stop()
Stops the timer.
Definition qtimer.cpp:226
\inmodule QtCore
Definition qurl.h:94
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1874
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1983
void setScheme(const QString &scheme)
Sets the scheme of the URL to scheme.
Definition qurl.cpp:1959
int port(int defaultPort=-1) const
Definition qurl.cpp:2380
static QUrl fromEncoded(QByteArrayView input, ParsingMode mode=TolerantMode)
Parses input and returns the corresponding QUrl.
Definition qurl.cpp:2985
void setPort(int port)
Sets the port of the URL to port.
Definition qurl.cpp:2355
\inmodule QtCore
Definition qvariant.h:64
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:707
qlonglong toLongLong(bool *ok=nullptr) const
Returns the variant as a long long int if the variant has userType() \l QMetaType::LongLong,...
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.
bool isNull() const
Returns true if this is a null variant, false otherwise.
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:531
QByteArray toByteArray() const
Returns the variant as a QByteArray if the variant has userType() \l QMetaType::QByteArray or \l QMet...
QSet< QString >::iterator it
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)
std::pair< T1, T2 > QPair
DBusConnection const char DBusError * error
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define forever
Definition qforeach.h:78
static bool isLWS(int c)
Definition qhsts.cpp:256
static bool isSeparator(char c)
Definition qhsts.cpp:278
#define rangeIndex(name)
Definition qlocale_p.h:452
#define qCritical
Definition qlogging.h:163
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
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(const QByteArray &text, int from)
static QHash< QByteArray, QByteArray > parseHttpOptionHeader(const QByteArray &header)
#define SLOT(a)
Definition qobjectdefs.h:51
#define Q_ARG(Type, data)
Definition qobjectdefs.h:62
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLsizei const GLfloat * v
[13]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLbitfield GLuint64 timeout
[4]
GLsizei range
GLenum GLuint buffer
GLenum GLenum dst
GLboolean enable
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLfloat n
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:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
#define Q_INT64_C(c)
Definition qtypes.h:52
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3503
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]
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:955
bool contains(const AT &t) const noexcept
Definition qlist.h:44
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...