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