MythTV  master
mythhttpcache.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QCryptographicHash>
3 
4 // MythTV
5 #include "mythlogging.h"
6 #include "mythdate.h"
7 #include "http/mythhttpdata.h"
8 #include "http/mythhttpfile.h"
9 #include "http/mythhttpcache.h"
10 
52 {
53  // Retrieve content
54  auto * data = std::get_if<HTTPData>(&Response->m_response);
55  auto * file = std::get_if<HTTPFile>(&Response->m_response);
56  if (!(file || data))
57  return;
58 
59  int cache = data ? (*data)->m_cacheType : (*file)->m_cacheType;
60 
61  // Explicitly ignore caching (error responses etc)
62  if (cache == HTTPIgnoreCache)
63  return;
64 
65  // Explicitly request no caching
66  if ((cache & HTTPNoCache) == HTTPNoCache)
67  return;
68 
69  // We use the last modified data for both the last modified header and potentially
70  // hashing for the etag
71  auto & lastmodified = data ? (*data)->m_lastModified : (*file)->m_lastModified;
72  if (!lastmodified.isValid())
73  lastmodified = QDateTime::currentDateTime();
74 
75  // If-Range precondition can contain either a last-modified or ETag validator
76  QString ifrange = MythHTTP::GetHeader(Response->m_requestHeaders, "if-range");
77  bool checkifrange = !ifrange.isEmpty();
78  bool removeranges = false;
79 
80  if ((cache & HTTPETag) == HTTPETag)
81  {
82  QByteArray& etag = data ? (*data)->m_etag : (*file)->m_etag;
83  if (file)
84  {
85  QByteArray hashdata = ((*file)->fileName() + lastmodified.toString("ddMMyyyyhhmmsszzz")).toLocal8Bit().constData();
86  etag = QCryptographicHash::hash(hashdata, QCryptographicHash::Sha224).toHex();
87  }
88  else
89  {
90  etag = QCryptographicHash::hash((*data)->constData(), QCryptographicHash::Sha224).toHex();
91  }
92 
93  // This assumes only one or other is present...
94  if (checkifrange)
95  {
96  removeranges = !ifrange.contains(etag);
97  }
98  else
99  {
100  QString nonematch = MythHTTP::GetHeader(Response->m_requestHeaders, "if-none-match");
101  if (!nonematch.isEmpty() && (nonematch.contains(etag)))
102  Response->m_status = HTTPNotModified;
103  }
104  }
105  else if (((cache & HTTPLastModified) == HTTPLastModified))
106  {
107  auto RemoveMilliseconds = [](QDateTime& DateTime)
108  {
109  auto residual = DateTime.time().msec();
110  DateTime = DateTime.addMSecs(-residual);
111  };
112 
113  auto ParseModified = [](const QString& Modified)
114  {
115  QDateTime time = QDateTime::fromString(Modified, Qt::RFC2822Date);
116 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
117  time.setTimeSpec(Qt::UTC);
118 #else
119  time.setTimeZone(QTimeZone(QTimeZone::UTC));
120 #endif
121  return time;
122  };
123 
124  if (checkifrange)
125  {
126  auto time = ParseModified(ifrange);
127  if (time.isValid())
128  {
129  RemoveMilliseconds(lastmodified);
130  removeranges = lastmodified > time;
131  }
132  }
133  else if (Response->m_requestType == HTTPGet || Response->m_requestType == HTTPHead)
134  {
135  QString modified = MythHTTP::GetHeader(Response->m_requestHeaders, "if-modified-since");
136  if (!modified.isEmpty())
137  {
138  auto time = ParseModified(modified);
139  if (time.isValid())
140  {
141  RemoveMilliseconds(lastmodified);
142  if (lastmodified <= time)
143  Response->m_status = HTTPNotModified;
144  }
145  }
146  }
147  }
148 
149  // If the If-Range precondition fails, then we need to send back the complete
150  // contents i.e. ignore any ranges. So search for the range headers and remove them.
151  if (removeranges && Response->m_requestHeaders && Response->m_requestHeaders->contains("range"))
152  Response->m_requestHeaders->replace("range", "");
153 }
154 
160 {
161  // Retrieve content
162  auto * data = std::get_if<HTTPData>(&Response->m_response);
163  auto * file = std::get_if<HTTPFile>(&Response->m_response);
164  if (!(file || data))
165  return;
166 
167  int cache = data ? (*data)->m_cacheType : (*file)->m_cacheType;
168 
169  // Explicitly ignore caching (error responses etc)
170  if (cache == HTTPIgnoreCache)
171  return;
172 
173  // Explicitly request no caching
174  if ((cache & HTTPNoCache) == HTTPNoCache)
175  {
176  Response->AddHeader("Cache-Control", "no-store, max-age=0");
177  return;
178  }
179 
180  // Add the default cache control header
181  QString duration {"0"}; // ??
182  if ((cache & HTTPLongLife) == HTTPLongLife)
183  duration = "604800"; // 7 days
184  else if ((cache & HTTPMediumLife) == HTTPMediumLife)
185  duration = "7200"; // 2 Hours
186  Response->AddHeader("Cache-Control", "no-cache=\"Ext\",max-age=" + duration);
187 
188  if ((cache & HTTPETag) == HTTPETag)
189  {
190  Response->AddHeader("ETag", QString("\"") + (data ? (*data)->m_etag : (*file)->m_etag) + "\"");
191  }
192  else if ((cache & HTTPLastModified) == HTTPLastModified)
193  {
194  auto & lastmodified = data ? (*data)->m_lastModified : (*file)->m_lastModified;
195  auto last = MythDate::toString(lastmodified, MythDate::kOverrideUTC | MythDate::kRFC822);
196  Response->AddHeader("Last-Modified", last);
197  }
198 }
mythhttpcache.h
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
HTTPGet
@ HTTPGet
Definition: mythhttptypes.h:94
MythHTTP::GetHeader
static QString GetHeader(const HTTPHeaders &Headers, const QString &Value, const QString &Default="")
Definition: mythhttptypes.h:318
MythDate::kOverrideUTC
@ kOverrideUTC
Present date/time in UTC.
Definition: mythdate.h:31
HTTPETag
@ HTTPETag
Definition: mythhttptypes.h:146
HTTPLongLife
@ HTTPLongLife
Definition: mythhttptypes.h:150
build_compdb.file
file
Definition: build_compdb.py:55
mythhttpdata.h
MythHTTPCache::PreConditionCheck
static void PreConditionCheck(const HTTPResponse &Response)
Process precondition checks.
Definition: mythhttpcache.cpp:51
mythhttpfile.h
mythdate.h
mythlogging.h
MythHTTPCache::PreConditionHeaders
static void PreConditionHeaders(const HTTPResponse &Response)
Add precondition (cache) headers to the response.
Definition: mythhttpcache.cpp:159
HTTPNoCache
@ HTTPNoCache
Definition: mythhttptypes.h:145
HTTPLastModified
@ HTTPLastModified
Definition: mythhttptypes.h:147
HTTPNotModified
@ HTTPNotModified
Definition: mythhttptypes.h:112
HTTPIgnoreCache
@ HTTPIgnoreCache
Definition: mythhttptypes.h:144
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
HTTPResponse
std::shared_ptr< MythHTTPResponse > HTTPResponse
Definition: mythhttptypes.h:40
HTTPHead
@ HTTPHead
Definition: mythhttptypes.h:93
MythDate::kRFC822
@ kRFC822
HTTP Date format.
Definition: mythdate.h:30
modified
static bool modified(uint64_t sig)
Definition: eitcache.cpp:91
HTTPMediumLife
@ HTTPMediumLife
Definition: mythhttptypes.h:149