diff options
author | randomdan <randomdan@localhost> | 2012-06-29 22:25:31 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2012-06-29 22:25:31 +0000 |
commit | 2c8a81381e301c7191a527efdd26ed2e57bf3487 (patch) | |
tree | 3e691a7f610ae6c3f0cb0a83980872a355415abc | |
parent | Bit of a tidy up (diff) | |
download | project2-2c8a81381e301c7191a527efdd26ed2e57bf3487.tar.bz2 project2-2c8a81381e301c7191a527efdd26ed2e57bf3487.tar.xz project2-2c8a81381e301c7191a527efdd26ed2e57bf3487.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.cpp | 34 | ||||
-rw-r--r-- | project2/cgi/cgiEnvironment.cpp | 6 | ||||
-rw-r--r-- | project2/cgi/cgiEnvironment.h | 1 | ||||
-rw-r--r-- | project2/cgi/cgiResult.h | 20 | ||||
-rw-r--r-- | project2/cgi/cgiResultStatic.cpp | 22 | ||||
-rw-r--r-- | project2/cgi/cgiResultWritable.cpp | 14 | ||||
-rw-r--r-- | project2/cgi/cgiStageCacheHit.cpp | 4 | ||||
-rw-r--r-- | project2/cgi/cgiStagePresent.cpp | 6 | ||||
-rw-r--r-- | project2/cgi/testCgi.cpp | 1 | ||||
-rw-r--r-- | project2/common/presenterCache.h | 1 | ||||
-rw-r--r-- | project2/common/transform.h | 1 | ||||
-rw-r--r-- | project2/files/presenterCache.cpp | 6 |
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]; |