// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include "nel/gui/http_cache.h" using namespace std; using namespace NLMISC; #ifdef DEBUG_NEW #define new DEBUG_NEW #endif #if defined(GCC_VERSION) && !defined(CLANG_VERSION) && defined(NL_ISO_CPP0X_AVAILABLE) && (GCC_VERSION <= 40804) // hack to fix std::map::erase wrong return type (void instead of iterator in C++11) in GCC 4.8.4 #undef NL_ISO_CPP0X_AVAILABLE #endif namespace NLGUI { CHttpCache* CHttpCache::instance = NULL; CHttpCache* CHttpCache::getInstance() { if (!instance) { instance = new CHttpCache(); } return instance; } void CHttpCache::release() { delete instance; instance = NULL; } CHttpCache::CHttpCache() : _Initialized(false) , _MaxObjects(100) { }; CHttpCache::~CHttpCache() { flushCache(); } void CHttpCache::setCacheIndex(const std::string& fname) { _IndexFilename = fname; _Initialized = false; } CHttpCacheObject CHttpCache::lookup(const std::string& fname) { if (!_Initialized) init(); if (_List.count(fname) > 0) return _List[fname]; return CHttpCacheObject(); } void CHttpCache::store(const std::string& fname, const CHttpCacheObject& data) { if (!_Initialized) init(); _List[fname] = data; } void CHttpCache::init() { if (_Initialized) return; _Initialized = true; if (_IndexFilename.empty() || !CFile::fileExists(_IndexFilename)) return; CIFile in; if (!in.open(_IndexFilename)) { nlwarning("Unable to open %s for reading", _IndexFilename.c_str()); return; } serial(in); } void CHttpCacheObject::serial(NLMISC::IStream& f) { f.serialVersion(1); f.serial(Expires); f.serial(LastModified); f.serial(Etag); } void CHttpCache::serial(NLMISC::IStream& f) { // saved state is ignored when version checks fail try { f.serialVersion(1); // CacheIdx f.serialCheck(NELID("hcaC")); f.serialCheck(NELID("xdIe")); if (f.isReading()) { uint32 numFiles; f.serial(numFiles); _List.clear(); for (uint k = 0; k < numFiles; ++k) { std::string fname; f.serial(fname); CHttpCacheObject obj; obj.serial(f); _List[fname] = obj; } } else { uint32 numFiles = _List.size(); f.serial(numFiles); for (THttpCacheMap::iterator it = _List.begin(); it != _List.end(); ++it) { std::string fname(it->first); f.serial(fname); (*it).second.serial(f); } } } catch (...) { _List.clear(); nlwarning("Invalid cache index format (%s)", _IndexFilename.c_str()); return; } } void CHttpCache::pruneCache() { if (_List.size() < _MaxObjects) return; size_t mustDrop = _List.size() - _MaxObjects; time_t currentTime; time(¤tTime); // if we over object limit, then start removing expired objects // this does not guarantee that max limit is reached for (THttpCacheMap::iterator it = _List.begin(); it != _List.end();) { if (it->second.Expires <= currentTime) { #ifdef NL_ISO_CPP0X_AVAILABLE it = _List.erase(it); #else THttpCacheMap::iterator itToErase = it++; _List.erase(itToErase); #endif --mustDrop; if (mustDrop == 0) break; } else { ++it; } } } void CHttpCache::flushCache() { if (_IndexFilename.empty()) return; pruneCache(); COFile out; if (!out.open(_IndexFilename)) { nlwarning("Unable to open %s for writing", _IndexFilename.c_str()); return; } serial(out); out.close(); } }