diff --git a/examples/CachingApp.cpp b/examples/CachingApp.cpp index 7d57618fc..1ac9942ee 100644 --- a/examples/CachingApp.cpp +++ b/examples/CachingApp.cpp @@ -5,13 +5,13 @@ int main() { uWS::App app; /* Regular, non-cached response */ - app.get("/*", [](auto *res, auto */*req*/) { + app.get("/not-cached", [](auto *res, auto */*req*/) { res->end("Responding without a cache"); - }).get("/cached", [](auto *res, auto */*req*/) { - /* A cached response with 13 seconds of lifetime */ + }).get("/*", [](auto *res, auto */*req*/) { + /* A cached response with 5 seconds of lifetime */ std::cout << "Filling cache now" << std::endl; res->end("This is a response"); - }, 13).listen(8080, [](bool success) { + }, 5).listen(8080, [](bool success) { if (success) { std::cout << "Listening on port 8080" << std::endl; } else { diff --git a/src/App.h b/src/App.h index d9852f402..685581b7f 100644 --- a/src/App.h +++ b/src/App.h @@ -598,8 +598,8 @@ struct TemplatedApp { #include "CachingApp.h" namespace uWS { - typedef uWS::CachingApp App; - typedef uWS::CachingApp SSLApp; + typedef uWS::CachingApp App; + typedef uWS::CachingApp SSLApp; } #endif // UWS_APP_H diff --git a/src/CachingApp.h b/src/CachingApp.h index fec5748d3..6571b4d2c 100644 --- a/src/CachingApp.h +++ b/src/CachingApp.h @@ -21,14 +21,12 @@ struct StringViewEqual { } }; -typedef std::unordered_map CacheType; + class CachingHttpResponse { public: - CachingHttpResponse(uWS::HttpResponse *res, CacheType &cache, const std::string_view &cacheKey) - : res(res), cache(cache), cacheKey(cacheKey) {} + CachingHttpResponse(uWS::HttpResponse *res) + : res(res) {} void write(std::string_view data) { buffer.append(data); @@ -36,26 +34,35 @@ class CachingHttpResponse { void end(std::string_view data = "") { buffer.append(data); - cache[cacheKey] = buffer; + + // end for all queued up sockets also res->end(buffer); + + created = time(0); } -private: - uWS::HttpResponse* res; - CacheType &cache; - std::string cacheKey; - std::string buffer; +public: + uWS::HttpResponse* res; // should be a vector of waiting sockets + + + std::string buffer; // body + time_t created; }; +typedef std::unordered_map CacheType; + // we can also derive from H3app later on -struct CachingApp : public uWS::TemplatedApp { +template +struct CachingApp : public uWS::TemplatedApp> { public: - CachingApp(SocketContextOptions options = {}) : uWS::TemplatedApp(options) {} + CachingApp(SocketContextOptions options = {}) : uWS::TemplatedApp>(options) {} - using uWS::TemplatedApp::get; + using uWS::TemplatedApp>::get; CachingApp(const CachingApp &other) = delete; - CachingApp(CachingApp &&other) : uWS::TemplatedApp(std::move(other)) { + CachingApp(CachingApp &&other) : uWS::TemplatedApp>(std::move(other)) { // also move the cache } @@ -64,27 +71,33 @@ struct CachingApp : public uWS::TemplatedApp { } // variant 1: only taking URL into account - CachingApp& get(const std::string& url, std::function handler, unsigned int /*secondsToExpiry*/) { - ((uWS::TemplatedApp *)this)->get(url, [this, handler](auto* res, auto* req) { + CachingApp& get(const std::string& url, std::function handler, unsigned int secondsToExpiry) { + ((uWS::TemplatedApp> *)this)->get(url, [this, handler, secondsToExpiry](auto* res, auto* req) { + /* We need to know the cache key and the time of now */ std::string_view cache_key = req->getFullUrl(); + time_t now = static_cast(us_loop_ext((us_loop_t *)uWS::Loop::get()))->cacheTimepoint; - // whenever a cache is to be used, its timestamp is checked against - // the one timestamp driven by the server - // CachedApp::updateTimestamp() will be called by the server timer - - // Loop::get()::getTimestamp() + auto it = cache.find(cache_key); + if (it != cache.end()) { - if (cache.find(cache_key) != cache.end()) { - res->end(cache[cache_key]); // tryEnd! - } else { + if (it->second->created + secondsToExpiry > now) { + res->end(it->second->buffer); // tryEnd! + return; + } - // om CacheNode har HttpRequest, status, headers, body - kan CacheNode användas som CachedHttpResponse - // Cache blir unorderd_map> cache; + /* We are no longer valid, delete old cache and fall through to create a new entry */ + delete it->second; - CachingHttpResponse cachingRes(res, cache, cache_key); // res kan inte vara stackallokerad + // is the cache completed? if not, add yourself to the waiting list of sockets to that cache - handler(&cachingRes, req); + // if the cache completed? ok, is it still valid? use it } + + // immediately take the place in the cache + CachingHttpResponse *cachingRes; + cache[cache_key] = (cachingRes = new CachingHttpResponse(res)); + + handler(cachingRes, req); }); return *this; } diff --git a/src/LoopData.h b/src/LoopData.h index 986bf0cbd..1b79659c7 100644 --- a/src/LoopData.h +++ b/src/LoopData.h @@ -61,13 +61,13 @@ struct alignas(16) LoopData { } void updateDate() { - time_t now = time(0); + cacheTimepoint = time(0); struct tm tstruct = {}; #ifdef _WIN32 /* Micro, fucking soft never follows spec. */ - gmtime_s(&tstruct, &now); + gmtime_s(&tstruct, &cacheTimepoint); #else - gmtime_r(&now, &tstruct); + gmtime_r(&cacheTimepoint, &tstruct); #endif static const char wday_name[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" @@ -87,6 +87,7 @@ struct alignas(16) LoopData { } char date[32]; + time_t cacheTimepoint = 0; /* Be silent */ bool noMark = false;