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 QNETWORKACCESSCACHEBACKEND_DEBUG |
5 | |
6 | #include "qnetworkaccesscachebackend_p.h" |
7 | #include "qabstractnetworkcache.h" |
8 | #include "qfileinfo.h" |
9 | #include "qdir.h" |
10 | #include "qcoreapplication.h" |
11 | #include "qhash.h" |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | using namespace Qt::StringLiterals; |
16 | |
17 | QNetworkAccessCacheBackend::QNetworkAccessCacheBackend() |
18 | : QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Local) |
19 | { |
20 | } |
21 | |
22 | QNetworkAccessCacheBackend::~QNetworkAccessCacheBackend() |
23 | { |
24 | } |
25 | |
26 | void QNetworkAccessCacheBackend::open() |
27 | { |
28 | if (operation() != QNetworkAccessManager::GetOperation || !sendCacheContents()) { |
29 | QString msg = QCoreApplication::translate(context: "QNetworkAccessCacheBackend" , key: "Error opening %1" ) |
30 | .arg(a: this->url().toString()); |
31 | error(code: QNetworkReply::ContentNotFoundError, errorString: msg); |
32 | } else { |
33 | setAttribute(attribute: QNetworkRequest::SourceIsFromCacheAttribute, value: true); |
34 | } |
35 | finished(); |
36 | } |
37 | |
38 | bool QNetworkAccessCacheBackend::sendCacheContents() |
39 | { |
40 | setCachingEnabled(false); |
41 | QAbstractNetworkCache *nc = networkCache(); |
42 | if (!nc) |
43 | return false; |
44 | |
45 | QNetworkCacheMetaData item = nc->metaData(url: url()); |
46 | if (!item.isValid()) |
47 | return false; |
48 | |
49 | QNetworkCacheMetaData::AttributesMap attributes = item.attributes(); |
50 | setAttribute(attribute: QNetworkRequest::HttpStatusCodeAttribute, |
51 | value: attributes.value(key: QNetworkRequest::HttpStatusCodeAttribute)); |
52 | setAttribute(attribute: QNetworkRequest::HttpReasonPhraseAttribute, |
53 | value: attributes.value(key: QNetworkRequest::HttpReasonPhraseAttribute)); |
54 | |
55 | // set the headers |
56 | auto = item.headers(); |
57 | const auto cacheControlValue = QLatin1StringView( |
58 | headers.value(name: QHttpHeaders::WellKnownHeader::CacheControl)); |
59 | // RFC 9111 Section 5.2 Cache Control |
60 | if (cacheControlValue.contains(s: "must-revalidate"_L1 , cs: Qt::CaseInsensitive) |
61 | || cacheControlValue.contains(s: "no-cache"_L1 , cs: Qt::CaseInsensitive)) { |
62 | return false; |
63 | } |
64 | setHeaders(std::move(headers)); |
65 | |
66 | // handle a possible redirect |
67 | QVariant redirectionTarget = attributes.value(key: QNetworkRequest::RedirectionTargetAttribute); |
68 | if (redirectionTarget.isValid()) { |
69 | setAttribute(attribute: QNetworkRequest::RedirectionTargetAttribute, value: redirectionTarget); |
70 | redirectionRequested(destination: redirectionTarget.toUrl()); |
71 | } |
72 | |
73 | // signal we're open |
74 | metaDataChanged(); |
75 | |
76 | if (operation() == QNetworkAccessManager::GetOperation) { |
77 | device = nc->data(url: url()); |
78 | if (!device) |
79 | return false; |
80 | device->setParent(this); |
81 | readyRead(); |
82 | } |
83 | |
84 | #if defined(QNETWORKACCESSCACHEBACKEND_DEBUG) |
85 | qDebug() << "Successfully sent cache:" << url(); |
86 | #endif |
87 | return true; |
88 | } |
89 | |
90 | bool QNetworkAccessCacheBackend::start() |
91 | { |
92 | open(); |
93 | return true; |
94 | } |
95 | |
96 | void QNetworkAccessCacheBackend::close() { } |
97 | |
98 | qint64 QNetworkAccessCacheBackend::bytesAvailable() const |
99 | { |
100 | return device ? device->bytesAvailable() : qint64(0); |
101 | } |
102 | |
103 | qint64 QNetworkAccessCacheBackend::read(char *data, qint64 maxlen) |
104 | { |
105 | return device ? device->read(data, maxlen) : qint64(0); |
106 | } |
107 | |
108 | QT_END_NAMESPACE |
109 | |
110 | |