summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2012-07-04 22:37:29 +0000
committerrandomdan <randomdan@localhost>2012-07-04 22:37:29 +0000
commit300d352c05cbbf7ddcebb62563ed1d3ca70e7878 (patch)
tree33623fd882161e522647116099a375e629c6199e
parentAdd support for client side caching and revalidation (HTTP If-Modified-Since,... (diff)
downloadproject2-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.cpp10
-rw-r--r--project2/cgi/cgiEnvironment.h4
-rw-r--r--project2/cgi/cgiResultStatic.cpp1
-rw-r--r--project2/cgi/cgiStagePresent.cpp4
-rw-r--r--project2/common/presenterCache.h3
-rw-r--r--project2/common/safeMapFind.h7
-rw-r--r--project2/common/transform.h1
-rw-r--r--project2/files/Jamfile.jam2
-rw-r--r--project2/files/presenterCache.cpp56
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