diff options
author | randomdan <randomdan@localhost> | 2012-07-04 22:37:29 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2012-07-04 22:37:29 +0000 |
commit | 300d352c05cbbf7ddcebb62563ed1d3ca70e7878 (patch) | |
tree | 33623fd882161e522647116099a375e629c6199e | |
parent | Add support for client side caching and revalidation (HTTP If-Modified-Since,... (diff) | |
download | project2-300d352c05cbbf7ddcebb62563ed1d3ca70e7878.tar.bz2 project2-300d352c05cbbf7ddcebb62563ed1d3ca70e7878.tar.xz project2-300d352c05cbbf7ddcebb62563ed1d3ca70e7878.zip |
Write sha1 hashes onto presenter cache files and validate client caches with them via etag
-rw-r--r-- | project2/cgi/cgiEnvironment.cpp | 10 | ||||
-rw-r--r-- | project2/cgi/cgiEnvironment.h | 4 | ||||
-rw-r--r-- | project2/cgi/cgiResultStatic.cpp | 1 | ||||
-rw-r--r-- | project2/cgi/cgiStagePresent.cpp | 4 | ||||
-rw-r--r-- | project2/common/presenterCache.h | 3 | ||||
-rw-r--r-- | project2/common/safeMapFind.h | 7 | ||||
-rw-r--r-- | project2/common/transform.h | 1 | ||||
-rw-r--r-- | project2/files/Jamfile.jam | 2 | ||||
-rw-r--r-- | project2/files/presenterCache.cpp | 56 |
9 files changed, 77 insertions, 11 deletions
diff --git a/project2/cgi/cgiEnvironment.cpp b/project2/cgi/cgiEnvironment.cpp index 725199b..af99f0d 100644 --- a/project2/cgi/cgiEnvironment.cpp +++ b/project2/cgi/cgiEnvironment.cpp @@ -1,5 +1,6 @@ #include <pch.hpp> #include <boost/version.hpp> +#include <boost/algorithm/string.hpp> #include "cgiEnvironment.h" #include "appEngine.h" #include "exceptions.h" @@ -114,6 +115,15 @@ CgiEnvironment::platform() const return hpi->derivedPlatform(); } +CgiEnvironment::ETags +CgiEnvironment::getRequestETags() const +{ + const std::string e = cgienv->getenv("HTTP_IF_NONE_MATCH"); + ETags etags; + boost::split(etags, e, boost::is_any_of(" ,"), boost::algorithm::token_compress_on); + return etags; +} + time_t CgiEnvironment::getRequestModifiedSince() const { diff --git a/project2/cgi/cgiEnvironment.h b/project2/cgi/cgiEnvironment.h index 5aded69..cfe9c7b 100644 --- a/project2/cgi/cgiEnvironment.h +++ b/project2/cgi/cgiEnvironment.h @@ -27,6 +27,9 @@ class HostnamePlatformIdentifier : public Options::Target { class CgiEnvironment : public Environment { public: + typedef std::string ETag; + typedef std::vector<ETag> ETags; + CgiEnvironment(); virtual ~CgiEnvironment(); @@ -37,6 +40,7 @@ class CgiEnvironment : public Environment { std::string getScriptName() const; std::string getRedirectURL() const; std::string getRequestMethod() const; + ETags getRequestETags() const; time_t getRequestModifiedSince() const; std::string getCookieValue(const std::string & name) const; diff --git a/project2/cgi/cgiResultStatic.cpp b/project2/cgi/cgiResultStatic.cpp index 542212e..b5fecd7 100644 --- a/project2/cgi/cgiResultStatic.cpp +++ b/project2/cgi/cgiResultStatic.cpp @@ -13,6 +13,7 @@ class StaticToCgiResult : public TransformImpl<StaticContent, CgiResult> { gmtime_r(&mtime, &stm); strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z", &stm); cr->header->addHeader("Last-Modified", buf); + cr->header->addHeader("Etag", sc->getSHA1()); cr->header->addHeader("Cache-Control", "must-revalidate"); cr->header->render(cr->stream); sc->writeTo(cr->stream); diff --git a/project2/cgi/cgiStagePresent.cpp b/project2/cgi/cgiStagePresent.cpp index d997e1d..1c6edaf 100644 --- a/project2/cgi/cgiStagePresent.cpp +++ b/project2/cgi/cgiStagePresent.cpp @@ -2,6 +2,7 @@ #include "cgiAppEngine.h" #include "cgiEnvironment.h" #include "cgiHttpHeader.h" +#include "safeMapFind.h" #include <boost/foreach.hpp> #include <boost/bind.hpp> @@ -23,9 +24,10 @@ CgiApplicationEngine::PresentStage::run() runChecks(); PresenterCaches backFill; time_t reqMS = this->env()->getRequestModifiedSince(); + CgiEnvironment::ETags etags = this->env()->getRequestETags(); BOOST_FOREACH(const PresenterCachePtr & pc, caches) { if (pc->check(root->script->modifiedTime())) { - if (reqMS >= pc->getModifiedTime()) { + if (reqMS >= pc->getModifiedTime() && (etags.empty() || containerContains(etags, pc->getSHA1()))) { header = HttpHeaderPtr(new Project2HttpHeader("304 Not Modified")); return NextStage(NULL, this, NULL, NULL); } diff --git a/project2/common/presenterCache.h b/project2/common/presenterCache.h index 891535c..15375da 100644 --- a/project2/common/presenterCache.h +++ b/project2/common/presenterCache.h @@ -5,11 +5,10 @@ #include "scripts.h" #include "iHaveParameters.h" -class PresenterCache : public SourceObject, public virtual TransformSource, public IHaveParameters { +class PresenterCache : public SourceObject, public virtual TransformSource, public StaticContent, public SourceOf<StaticContent>, public IHaveParameters { 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/safeMapFind.h b/project2/common/safeMapFind.h index b461074..a5786bc 100644 --- a/project2/common/safeMapFind.h +++ b/project2/common/safeMapFind.h @@ -34,5 +34,12 @@ safeMapLookup(const Map & map, const typename Map::key_type & key) return i->second; } +template <class Cont> +bool +containerContains(const Cont & c, const typename Cont::value_type & v) +{ + return (std::find(c.begin(), c.end(), v) != c.end()); +} + #endif diff --git a/project2/common/transform.h b/project2/common/transform.h index 2061374..ef8fe83 100644 --- a/project2/common/transform.h +++ b/project2/common/transform.h @@ -83,6 +83,7 @@ class StaticContent { virtual Glib::ustring getContentType() const = 0; virtual Glib::ustring getEncoding() const = 0; virtual time_t getModifiedTime() const = 0; + virtual std::string getSHA1() const = 0; virtual size_t getSizeInBytes() const = 0; virtual void writeTo(std::ostream &) const = 0; }; diff --git a/project2/files/Jamfile.jam b/project2/files/Jamfile.jam index bdb851f..b8ae31e 100644 --- a/project2/files/Jamfile.jam +++ b/project2/files/Jamfile.jam @@ -5,6 +5,7 @@ alias glibmm : : : : lib boost_system : : <name>boost_system ; lib boost_filesystem : : <name>boost_filesystem ; lib boost_iostreams : : <name>boost_iostreams ; +lib gcrypt : : <name>gcrypt ; cpp-pch pch : pch.hpp : <library>../common//p2common @@ -18,6 +19,7 @@ lib p2files : <library>boost_filesystem <library>boost_system <library>boost_iostreams + <library>gcrypt <library>../common//p2common : : <include>. diff --git a/project2/files/presenterCache.cpp b/project2/files/presenterCache.cpp index d475824..90c2bc9 100644 --- a/project2/files/presenterCache.cpp +++ b/project2/files/presenterCache.cpp @@ -14,11 +14,13 @@ #include <boost/tuple/tuple_comparison.hpp> #include <glibmm/convert.h> #include <sys/file.h> +#include <gcrypt.h> SimpleSysCallException(OpenCacheFile); SimpleSysCallException(StatCacheFile); SimpleSysCallException(LockCacheFile); SimpleSysCallException(TruncateCacheFile); +SimpleSysCallException(WriteCacheFile); SimpleSysCallException(CacheFileXAttr); template <class E> @@ -29,7 +31,7 @@ int safesys(int n, int r) { return r; } -class FilePresenterCache : public PresenterCache, public StaticContent, public SourceOf<StaticContent> { +class FilePresenterCache : public PresenterCache { public: typedef std::vector<std::string> Path; typedef std::map<std::string, VariableType> Parameters; @@ -47,13 +49,46 @@ class FilePresenterCache : public PresenterCache, public StaticContent, public S typedef boost::intrusive_ptr<CacheFile> CacheFilePtr; typedef std::map<Key, CacheFilePtr> OpenCaches; + class WriteCacheFile : public boost::iostreams::sink { + public: + WriteCacheFile(int f) : fd(f) { + gcry_md_open(&state, GCRY_MD_SHA1, 0); + } + WriteCacheFile(const WriteCacheFile & wcf) : fd(dup(wcf.fd)) { + gcry_md_copy(&state, wcf.state); + } + ~WriteCacheFile() { + gcry_md_close(state); + ::close(fd); + } + std::streamsize write(const char * data, std::streamsize len) + { + gcry_md_write(state, data, len); + return safesys<WriteCacheFile>(-1, ::write(fd, data, len)); + } + std::string hash() const + { + int hash_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1); + unsigned char * hash = gcry_md_read(state, GCRY_MD_SHA1); + std::string hashstr(hash_len * 2, '-'); + char * p = &hashstr[0]; + for (int i = 0; i < hash_len; i++, p += 2) { + snprintf(p, 3, "%02x", hash[i]); + } + return hashstr; + } + const int fd; + private: + gcry_md_hd_t state; + }; + typedef boost::iostreams::stream<WriteCacheFile> WriteCacheStrm; + typedef boost::shared_ptr<WriteCacheStrm> WriteCacheStrmPtr; + FilePresenterCache(ScriptNodePtr s) : - PresenterCache(s), - writecache(NULL) { + PresenterCache(s) { } ~FilePresenterCache() { - delete writecache; } bool check(time_t scriptMtime) const { @@ -131,15 +166,20 @@ class FilePresenterCache : public PresenterCache, public StaticContent, public S safesys<TruncateCacheFile>(-1, ::ftruncate(fd, 0)); safesys<CacheFileXAttr>(-1, fsetxattr(fd, "user.content-type", ct.c_str(), ct.length(), 0)); safesys<CacheFileXAttr>(-1, fsetxattr(fd, "user.encoding", enc.c_str(), enc.length(), 0)); - writecache = new boost::iostreams::stream<boost::iostreams::file_descriptor_sink>(fd, boost::iostreams::close_handle); + writecache = WriteCacheStrmPtr(new boost::iostreams::stream<WriteCacheFile>(fd)); return *writecache; } void flushCache() { - delete writecache; - writecache = NULL; + std::string hash((*writecache)->hash()); + safesys<CacheFileXAttr>(-1, fsetxattr((*writecache)->fd, "user.sha1", hash.c_str(), hash.length(), 0)); + writecache.reset(); + } + std::string getSHA1() const + { + return getXAttr("user.sha1"); } - std::ostream * writecache; + WriteCacheStrmPtr writecache; private: Key getCacheKey() const |