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  time.setTimeSpec(Qt::OffsetFromUTC);
117  return time;
118  };
119 
120  if (checkifrange)
121  {
122  auto time = ParseModified(ifrange);
123  if (time.isValid())
124  {
125  RemoveMilliseconds(lastmodified);
126  removeranges = lastmodified > time;
127  }
128  }
129  else if (Response->m_requestType == HTTPGet || Response->m_requestType == HTTPHead)
130  {
131  QString modified = MythHTTP::GetHeader(Response->m_requestHeaders, "if-modified-since");
132  if (!modified.isEmpty())
133  {
134  auto time = ParseModified(modified);
135  if (time.isValid())
136  {
137  RemoveMilliseconds(lastmodified);
138  if (lastmodified <= time)
139  Response->m_status = HTTPNotModified;
140  }
141  }
142  }
143  }
144 
145  // If the If-Range precondition fails, then we need to send back the complete
146  // contents i.e. ignore any ranges. So search for the range headers and remove them.
147  if (removeranges && Response->m_requestHeaders && Response->m_requestHeaders->contains("range"))
148  Response->m_requestHeaders->replace("range", "");
149 }
150 
156 {
157  // Retrieve content
158  auto * data = std::get_if<HTTPData>(&Response->m_response);
159  auto * file = std::get_if<HTTPFile>(&Response->m_response);
160  if (!(file || data))
161  return;
162 
163  int cache = data ? (*data)->m_cacheType : (*file)->m_cacheType;
164 
165  // Explicitly ignore caching (error responses etc)
166  if (cache == HTTPIgnoreCache)
167  return;
168 
169  // Explicitly request no caching
170  if ((cache & HTTPNoCache) == HTTPNoCache)
171  {
172  Response->AddHeader("Cache-Control", "no-store, max-age=0");
173  return;
174  }
175 
176  // Add the default cache control header
177  QString duration = ((cache & HTTPLongLife) == HTTPLongLife) ? "604800" : // 7 days
178  ((cache & HTTPMediumLife) == HTTPMediumLife) ? "7200" : // 2 Hours
179  "0"; // ??
180  Response->AddHeader("Cache-Control", "no-cache=\"Ext\",max-age=" + duration);
181 
182  if ((cache & HTTPETag) == HTTPETag)
183  {
184  Response->AddHeader("ETag", QString("\"") + (data ? (*data)->m_etag : (*file)->m_etag) + "\"");
185  }
186  else if ((cache & HTTPLastModified) == HTTPLastModified)
187  {
188  auto & lastmodified = data ? (*data)->m_lastModified : (*file)->m_lastModified;
189  auto last = MythDate::toString(lastmodified, MythDate::kOverrideUTC | MythDate::kRFC822);
190  Response->AddHeader("Last-Modified", last);
191  }
192 }
mythhttpcache.h
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:84
HTTPGet
@ HTTPGet
Definition: mythhttptypes.h:93
MythHTTP::GetHeader
static QString GetHeader(const HTTPHeaders &Headers, const QString &Value, const QString &Default="")
Definition: mythhttptypes.h:317
MythDate::kOverrideUTC
@ kOverrideUTC
Present date/time in UTC.
Definition: mythdate.h:31
HTTPETag
@ HTTPETag
Definition: mythhttptypes.h:145
HTTPLongLife
@ HTTPLongLife
Definition: mythhttptypes.h:149
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:155
HTTPNoCache
@ HTTPNoCache
Definition: mythhttptypes.h:144
HTTPLastModified
@ HTTPLastModified
Definition: mythhttptypes.h:146
HTTPNotModified
@ HTTPNotModified
Definition: mythhttptypes.h:111
HTTPIgnoreCache
@ HTTPIgnoreCache
Definition: mythhttptypes.h:143
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:34
HTTPResponse
std::shared_ptr< MythHTTPResponse > HTTPResponse
Definition: mythhttptypes.h:39
HTTPHead
@ HTTPHead
Definition: mythhttptypes.h:92
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:148