summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2012-06-29 22:25:31 +0000
committerrandomdan <randomdan@localhost>2012-06-29 22:25:31 +0000
commit6310009f0c79614fab5214fcc817bcf4901147f6 (patch)
tree3e691a7f610ae6c3f0cb0a83980872a355415abc
parentBit of a tidy up (diff)
downloadproject2-6310009f0c79614fab5214fcc817bcf4901147f6.tar.bz2
project2-6310009f0c79614fab5214fcc817bcf4901147f6.tar.xz
project2-6310009f0c79614fab5214fcc817bcf4901147f6.zip
Add support for client side caching and revalidation (HTTP If-Modified-Since, must-revalidate and 304 Not Modified)
-rw-r--r--project2/cgi/cgiAppEngine.cpp34
-rw-r--r--project2/cgi/cgiEnvironment.cpp6
-rw-r--r--project2/cgi/cgiEnvironment.h1
-rw-r--r--project2/cgi/cgiResult.h20
-rw-r--r--project2/cgi/cgiResultStatic.cpp22
-rw-r--r--project2/cgi/cgiResultWritable.cpp14
-rw-r--r--project2/cgi/cgiStageCacheHit.cpp4
-rw-r--r--project2/cgi/cgiStagePresent.cpp6
-rw-r--r--project2/cgi/testCgi.cpp1
-rw-r--r--project2/common/presenterCache.h1
-rw-r--r--project2/common/transform.h1
-rw-r--r--project2/files/presenterCache.cpp6
12 files changed, 79 insertions, 37 deletions
diff --git a/project2/cgi/cgiAppEngine.cpp b/project2/cgi/cgiAppEngine.cpp
index cc7a3aa..d9948e4 100644
--- a/project2/cgi/cgiAppEngine.cpp
+++ b/project2/cgi/cgiAppEngine.cpp
@@ -1,5 +1,6 @@
#include <pch.hpp>
#include "cgiAppEngine.h"
+#include "cgiResult.h"
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHeader.h>
#include "cgiEnvironment.h"
@@ -41,39 +42,6 @@ CgiApplicationEngine::env() const
return _env;
}
-class CgiResult : public TransformChainLink {
- public:
- CgiResult(CgiApplicationEngine::HttpHeaderPtr & h, std::ostream & s, const std::string & e) :
- header(h),
- stream(s),
- encoding(e) {
- }
- CgiApplicationEngine::HttpHeaderPtr header;
- std::ostream & stream;
- const std::string encoding;
-};
-
-class WritableToCgiResult : public TransformImpl<WritableContent, CgiResult> {
- public:
- void transform(const WritableContent * wc, CgiResult * cr) const {
- cr->header->addHeader("Content-Type", Glib::ustring::compose("%1; charset=%2", wc->getContentType(), cr->encoding));
- cr->header->render(cr->stream);
- wc->writeTo(cr->stream, cr->encoding);
- }
-};
-DECLARE_TRANSFORM(WritableToCgiResult);
-
-class StaticToCgiResult : public TransformImpl<StaticContent, CgiResult> {
- public:
- void transform(const StaticContent * sc, CgiResult * cr) const {
- cr->header->addHeader("Content-Type", Glib::ustring::compose("%1; charset=%2", sc->getContentType(), sc->getEncoding()));
- cr->header->addHeader("Content-Length", Glib::ustring::compose("%1", sc->getSizeInBytes()));
- cr->header->render(cr->stream);
- sc->writeTo(cr->stream);
- }
-};
-DECLARE_TRANSFORM(StaticToCgiResult);
-
TransformSourcePtr
finalTransformSource(TransformSourcePtr ts)
{
diff --git a/project2/cgi/cgiEnvironment.cpp b/project2/cgi/cgiEnvironment.cpp
index a5d4af2..725199b 100644
--- a/project2/cgi/cgiEnvironment.cpp
+++ b/project2/cgi/cgiEnvironment.cpp
@@ -6,6 +6,7 @@
#include <map>
#include <cgicc/Cgicc.h>
#include <glibmm/regex.h>
+#include <curl/curl.h>
std::vector<std::string>
makeVector(const boost::filesystem::path & y)
@@ -113,6 +114,11 @@ CgiEnvironment::platform() const
return hpi->derivedPlatform();
}
+time_t
+CgiEnvironment::getRequestModifiedSince() const
+{
+ return curl_getdate(cgienv->getenv("HTTP_IF_MODIFIED_SINCE").c_str(), NULL);
+}
Glib::ustring
CgiEnvironment::getParamUri(unsigned int p) const
diff --git a/project2/cgi/cgiEnvironment.h b/project2/cgi/cgiEnvironment.h
index be6732c..5aded69 100644
--- a/project2/cgi/cgiEnvironment.h
+++ b/project2/cgi/cgiEnvironment.h
@@ -37,6 +37,7 @@ class CgiEnvironment : public Environment {
std::string getScriptName() const;
std::string getRedirectURL() const;
std::string getRequestMethod() const;
+ time_t getRequestModifiedSince() const;
std::string getCookieValue(const std::string & name) const;
void setCGICC(const cgicc::Cgicc *, const CgiEnvInput * cgienv);
diff --git a/project2/cgi/cgiResult.h b/project2/cgi/cgiResult.h
new file mode 100644
index 0000000..35165c8
--- /dev/null
+++ b/project2/cgi/cgiResult.h
@@ -0,0 +1,20 @@
+#ifndef CGIRESULT_H
+#define CGIRESULT_H
+
+#include "transform.h"
+#include "cgiAppEngine.h"
+
+class CgiResult : public TransformChainLink {
+ public:
+ CgiResult(CgiApplicationEngine::HttpHeaderPtr & h, std::ostream & s, const std::string & e) :
+ header(h),
+ stream(s),
+ encoding(e) {
+ }
+ CgiApplicationEngine::HttpHeaderPtr header;
+ std::ostream & stream;
+ const std::string encoding;
+};
+
+#endif
+
diff --git a/project2/cgi/cgiResultStatic.cpp b/project2/cgi/cgiResultStatic.cpp
new file mode 100644
index 0000000..542212e
--- /dev/null
+++ b/project2/cgi/cgiResultStatic.cpp
@@ -0,0 +1,22 @@
+#include <pch.hpp>
+#include <time.h>
+#include "cgiResult.h"
+
+class StaticToCgiResult : public TransformImpl<StaticContent, CgiResult> {
+ public:
+ void transform(const StaticContent * sc, CgiResult * cr) const {
+ cr->header->addHeader("Content-Type", Glib::ustring::compose("%1; charset=%2", sc->getContentType(), sc->getEncoding()));
+ cr->header->addHeader("Content-Length", Glib::ustring::compose("%1", sc->getSizeInBytes()));
+ char buf[100];
+ struct tm stm;
+ time_t mtime = sc->getModifiedTime();
+ gmtime_r(&mtime, &stm);
+ strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z", &stm);
+ cr->header->addHeader("Last-Modified", buf);
+ cr->header->addHeader("Cache-Control", "must-revalidate");
+ cr->header->render(cr->stream);
+ sc->writeTo(cr->stream);
+ }
+};
+DECLARE_TRANSFORM(StaticToCgiResult);
+
diff --git a/project2/cgi/cgiResultWritable.cpp b/project2/cgi/cgiResultWritable.cpp
new file mode 100644
index 0000000..ea17d3b
--- /dev/null
+++ b/project2/cgi/cgiResultWritable.cpp
@@ -0,0 +1,14 @@
+#include <pch.hpp>
+#include "cgiResult.h"
+
+class WritableToCgiResult : public TransformImpl<WritableContent, CgiResult> {
+ public:
+ void transform(const WritableContent * wc, CgiResult * cr) const {
+ cr->header->addHeader("Content-Type", Glib::ustring::compose("%1; charset=%2", wc->getContentType(), cr->encoding));
+ cr->header->addHeader("Cache-control", "no-cache");
+ cr->header->render(cr->stream);
+ wc->writeTo(cr->stream, cr->encoding);
+ }
+};
+DECLARE_TRANSFORM(WritableToCgiResult);
+
diff --git a/project2/cgi/cgiStageCacheHit.cpp b/project2/cgi/cgiStageCacheHit.cpp
index 8d854b6..d398e8f 100644
--- a/project2/cgi/cgiStageCacheHit.cpp
+++ b/project2/cgi/cgiStageCacheHit.cpp
@@ -20,8 +20,6 @@ CgiApplicationEngine::CacheHitStage::run()
CgiApplicationEngine::HttpHeaderPtr
CgiApplicationEngine::CacheHitStage::getHeader() const
{
- Project2HttpHeader * header = new Project2HttpHeader("200 OK");
- header->addHeader("Cache-control", "no-cache");
- return HttpHeaderPtr(header);
+ return HttpHeaderPtr(new Project2HttpHeader("200 OK"));
}
diff --git a/project2/cgi/cgiStagePresent.cpp b/project2/cgi/cgiStagePresent.cpp
index b6bb11e..d997e1d 100644
--- a/project2/cgi/cgiStagePresent.cpp
+++ b/project2/cgi/cgiStagePresent.cpp
@@ -22,8 +22,13 @@ CgiApplicationEngine::PresentStage::run()
{
runChecks();
PresenterCaches backFill;
+ time_t reqMS = this->env()->getRequestModifiedSince();
BOOST_FOREACH(const PresenterCachePtr & pc, caches) {
if (pc->check(root->script->modifiedTime())) {
+ if (reqMS >= pc->getModifiedTime()) {
+ header = HttpHeaderPtr(new Project2HttpHeader("304 Not Modified"));
+ return NextStage(NULL, this, NULL, NULL);
+ }
CacheHitStage * chs = new CacheHitStage(root, pc);
chs->caches = backFill;
return NextStage(NULL, chs, pc, NULL);
@@ -63,7 +68,6 @@ CgiApplicationEngine::ResponseStage::ResponseStage(ScriptNodePtr r) :
CgiApplicationEngine::HttpHeaderPtr
CgiApplicationEngine::PresentStage::getHeader() const
{
- header->addHeader("Cache-control", "no-cache");
return header;
}
diff --git a/project2/cgi/testCgi.cpp b/project2/cgi/testCgi.cpp
index 8a4ccb6..fb480a4 100644
--- a/project2/cgi/testCgi.cpp
+++ b/project2/cgi/testCgi.cpp
@@ -56,6 +56,7 @@ class TestInput : public cgicc::CgiInput, public CgiEnvInput {
TESTOPT("HTTP_REFERER", "", "Referrer")
TESTOPT("HTTP_COOKIE", "", "Cookie")
TESTOPT("HTTPS", "No", "HTTPS?")
+ TESTOPT("HTTP_IF_MODIFIED_SINCE", "", "Client cached copy timestamp")
("runCount", Options::value(&runCount, 1), "Repeat run this many times")
;
FileOptions fo(".testCgi.settings");
diff --git a/project2/common/presenterCache.h b/project2/common/presenterCache.h
index 63ff9f0..891535c 100644
--- a/project2/common/presenterCache.h
+++ b/project2/common/presenterCache.h
@@ -9,6 +9,7 @@ class PresenterCache : public SourceObject, public virtual TransformSource, publ
public:
PresenterCache(ScriptNodePtr);
virtual bool check(time_t scriptMtime) const = 0;
+ virtual time_t getModifiedTime() const = 0;
virtual std::ostream & writeCache(const std::string & ct, const std::string & encoding) = 0;
virtual void flushCache();
diff --git a/project2/common/transform.h b/project2/common/transform.h
index 973f4f0..2061374 100644
--- a/project2/common/transform.h
+++ b/project2/common/transform.h
@@ -82,6 +82,7 @@ class StaticContent {
public:
virtual Glib::ustring getContentType() const = 0;
virtual Glib::ustring getEncoding() const = 0;
+ virtual time_t getModifiedTime() const = 0;
virtual size_t getSizeInBytes() const = 0;
virtual void writeTo(std::ostream &) const = 0;
};
diff --git a/project2/files/presenterCache.cpp b/project2/files/presenterCache.cpp
index 9c8ace2..d475824 100644
--- a/project2/files/presenterCache.cpp
+++ b/project2/files/presenterCache.cpp
@@ -93,6 +93,12 @@ class FilePresenterCache : public PresenterCache, public StaticContent, public S
{
return getXAttr("user.content-type");
}
+ time_t getModifiedTime() const
+ {
+ struct stat st;
+ safesys<CacheFileXAttr>(-1, fstat(*myCache, &st));
+ return st.st_mtime;
+ }
Glib::ustring getXAttr(const char * attr) const
{
char buf[BUFSIZ];