[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/sony/easyhttpcpp/master/src/HttpCacheDatabase.cpp [Back]  [Original]

/*
 * Copyright 2017 Sony Corporation
 */

#include "Poco/Timestamp.h"

#include "easyhttpcpp/common/CoreLogger.h"
#include "easyhttpcpp/common/FileUtil.h"
#include "easyhttpcpp/db/AutoSqliteCursor.h"
#include "easyhttpcpp/db/AutoSqliteDatabase.h"
#include "easyhttpcpp/db/AutoSqliteTransaction.h"
#include "easyhttpcpp/db/ContentValues.h"
#include "easyhttpcpp/db/SqlException.h"
#include "easyhttpcpp/HttpException.h"

#include "HttpCacheDatabase.h"
#include "HttpCacheMetadata.h"
#include "HttpCacheEnumerationListener.h"
#include "HttpInternalConstants.h"
#include "HttpUtil.h"

using easyhttpcpp::common::Cache;
using easyhttpcpp::common::CacheMetadata;
using easyhttpcpp::common::FileUtil;
using easyhttpcpp::db::AutoSqliteCursor;
using easyhttpcpp::db::AutoSqliteDatabase;
using easyhttpcpp::db::AutoSqliteTransaction;
using easyhttpcpp::db::ContentValues;
using easyhttpcpp::db::SqlException;
using easyhttpcpp::db::SqliteCursor;
using easyhttpcpp::db::SqliteDatabase;
using easyhttpcpp::db::SqliteOpenHelper;

namespace easyhttpcpp {

static const std::string Tag = "HttpCacheDatabase";

HttpCacheDatabase::HttpCacheDatabase(HttpCacheDatabaseOpenHelper::Ptr pOpenHelper) : m_pOpenHelper(pOpenHelper)
{
}

HttpCacheDatabase::~HttpCacheDatabase()
{
}

HttpCacheMetadata::Ptr HttpCacheDatabase::getMetadata(const std::string& key)
{
    Poco::FastMutex::ScopedLock lock(m_mutex);

    SqliteDatabase::Ptr pDb;
    try {
        pDb = m_pOpenHelper->getReadableDatabase();
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "getMetadata() Unable to open database. Error: %s", e.getMessage().c_str());
        throw;
    }
    AutoSqliteDatabase autoSqliteDatabase(pDb);

    try {
        std::vector columns;
        columns.push_back(HttpInternalConstants::Database::Key::Url);
        columns.push_back(HttpInternalConstants::Database::Key::Method);
        columns.push_back(HttpInternalConstants::Database::Key::StatusCode);
        columns.push_back(HttpInternalConstants::Database::Key::StatusMessage);
        columns.push_back(HttpInternalConstants::Database::Key::ResponseHeaderJson);
        columns.push_back(HttpInternalConstants::Database::Key::ResponseBodySize);
        columns.push_back(HttpInternalConstants::Database::Key::SentRequestAtEpoch);
        columns.push_back(HttpInternalConstants::Database::Key::ReceivedResponseAtEpoch);
        columns.push_back(HttpInternalConstants::Database::Key::CreatedAtEpoch);

        std::string whereClause = std::string(HttpInternalConstants::Database::Key::CacheKey) + "=?";
        std::vector whereArgs;
        whereArgs.push_back(key);

        SqliteCursor::Ptr pCursor = pDb->query(HttpInternalConstants::Database::TableName, &columns, &whereClause,
                &whereArgs, NULL, NULL, NULL, NULL);
        AutoSqliteCursor autoSqliteCursor(pCursor);
        if (pCursor->moveToFirst()) {
            HttpCacheMetadata::Ptr pHttpCacheMetadata = new HttpCacheMetadata();
            pHttpCacheMetadata->setKey(key);
            pHttpCacheMetadata->setUrl(pCursor->getString(0));
            pHttpCacheMetadata->setHttpMethod(static_cast (pCursor->getInt(1)));
            pHttpCacheMetadata->setStatusCode(pCursor->getInt(2));
            pHttpCacheMetadata->setStatusMessage(pCursor->getString(3));
            pHttpCacheMetadata->setResponseHeaders(HttpUtil::exchangeJsonStrToHeaders(pCursor->getString(4)));
            pHttpCacheMetadata->setResponseBodySize(pCursor->getUnsignedLongLong(5));
            pHttpCacheMetadata->setSentRequestAtEpoch(pCursor->getUnsignedLongLong(6));
            pHttpCacheMetadata->setReceivedResponseAtEpoch(pCursor->getUnsignedLongLong(7));
            pHttpCacheMetadata->setCreatedAtEpoch(pCursor->getUnsignedLongLong(8));

            // dump
            EASYHTTPCPP_LOG_D(Tag, "getMetadata");
            dumpMetadata(pHttpCacheMetadata);
            return pHttpCacheMetadata;
        } else {
            EASYHTTPCPP_LOG_D(Tag, "getMetadata(): can not get row");
            return NULL;
        }
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "SQLite error while getMetadata(): %s", e.getMessage().c_str());
        throw;
    }
}

bool HttpCacheDatabase::deleteMetadata(const std::string& key)
{
    Poco::FastMutex::ScopedLock lock(m_mutex);

    SqliteDatabase::Ptr pDb;
    try {
        pDb = m_pOpenHelper->getWritableDatabase();
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "deleteMetadata() Unable to open database. Error: %s", e.getMessage().c_str());
        throw;
    }
    AutoSqliteDatabase autoSqliteDatabase(pDb);

    try {
        AutoSqliteTransaction autoSqliteTransaction(pDb);

        std::string whereClause = std::string(HttpInternalConstants::Database::Key::CacheKey) + "=?";
        std::vector whereArgs;
        whereArgs.push_back(key);

        bool deleted = pDb->deleteRows(HttpInternalConstants::Database::TableName, &whereClause, &whereArgs) > 0;

        pDb->setTransactionSuccessful();

        return deleted;
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "SQLite error while deleteMetadata(): %s", e.getMessage().c_str());
        // do not set the transaction as successful; database will rollback automatically
        throw;
    }
}

void HttpCacheDatabase::updateMetadata(const std::string& key, HttpCacheMetadata::Ptr pHttpCacheMetadata)
{
    Poco::FastMutex::ScopedLock lock(m_mutex);

    SqliteDatabase::Ptr pDb;
    try {
        pDb = m_pOpenHelper->getWritableDatabase();
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "updateMetadata() Unable to open database. Error: %s", e.getMessage().c_str());
        throw;
    }
    AutoSqliteDatabase autoSqliteDatabase(pDb);

    try {
        // always use transactions for speedy and reliable updates
        AutoSqliteTransaction autoSqliteTransaction(pDb);

        ContentValues values;
        values.put(HttpInternalConstants::Database::Key::CacheKey, pHttpCacheMetadata->getKey());
        values.put(HttpInternalConstants::Database::Key::Url, pHttpCacheMetadata->getUrl());
        values.put(HttpInternalConstants::Database::Key::Method, pHttpCacheMetadata->getHttpMethod());
        values.put(HttpInternalConstants::Database::Key::StatusCode, pHttpCacheMetadata->getStatusCode());
        values.put(HttpInternalConstants::Database::Key::StatusMessage, pHttpCacheMetadata->getStatusMessage());
        values.put(HttpInternalConstants::Database::Key::ResponseHeaderJson,
                HttpUtil::exchangeHeadersToJsonStr(pHttpCacheMetadata->getResponseHeaders()));
        values.put(HttpInternalConstants::Database::Key::ResponseBodySize, pHttpCacheMetadata->getResponseBodySize());
        values.put(HttpInternalConstants::Database::Key::SentRequestAtEpoch,
                pHttpCacheMetadata->getSentRequestAtEpoch());
        values.put(HttpInternalConstants::Database::Key::ReceivedResponseAtEpoch,
                pHttpCacheMetadata->getReceivedResponseAtEpoch());
        values.put(HttpInternalConstants::Database::Key::CreatedAtEpoch, pHttpCacheMetadata->getCreatedAtEpoch());
        Poco::Timestamp now;
        values.put(HttpInternalConstants::Database::Key::LastAccessedAtEpoch, now.epochTime());

        // do an INSERT, and if that INSERT fails because of a conflict,
        // delete the conflicting rows before INSERTing again
        pDb->replace(HttpInternalConstants::Database::TableName, values);
        EASYHTTPCPP_LOG_V(Tag, "New HttpCacheMetadata updateMetadata.");

        pDb->setTransactionSuccessful();

        // dump
        EASYHTTPCPP_LOG_D(Tag, "updateMetadata");
        dumpMetadata(pHttpCacheMetadata);
        EASYHTTPCPP_LOG_D(Tag, "lastAccessedAtEpoch = %s", Poco::DateTimeFormatter::format(now,
                Poco::DateTimeFormat::HTTP_FORMAT).c_str());

    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "SQLite error while updateMetadata(): %s", e.getMessage().c_str());
        throw;
    }
}

bool HttpCacheDatabase::updateLastAccessedSec(const std::string& key)
{
    Poco::FastMutex::ScopedLock lock(m_mutex);

    SqliteDatabase::Ptr pDb;
    try {
        pDb = m_pOpenHelper->getWritableDatabase();
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "updateMetadata() Unable to open database. Error: %s", e.getMessage().c_str());
        throw;
    }
    AutoSqliteDatabase autoSqliteDatabase(pDb);

    try {
        // always use transactions for speedy and reliable updates
        AutoSqliteTransaction autoSqliteTransaction(pDb);

        ContentValues values;
        Poco::Timestamp now;
        values.put(HttpInternalConstants::Database::Key::LastAccessedAtEpoch, now.epochTime());

        EASYHTTPCPP_LOG_D(Tag, "updateLastAccessedSec: lastAccessedAtEpoch = %s", Poco::DateTimeFormatter::format(now,
                Poco::DateTimeFormat::HTTP_FORMAT).c_str());

        std::string whereClause = std::string(HttpInternalConstants::Database::Key::CacheKey) + "=?";
        std::vector whereArgs;
        whereArgs.push_back(key);

        bool update;
        if (pDb->update(HttpInternalConstants::Database::TableName, values, &whereClause, &whereArgs) == 0) {
            // cannot update lastAccessedSec in cache, but ignore it.
            EASYHTTPCPP_LOG_D(Tag, "updateLastAccessedSec : can not update database.");
            update = false;
        } else {
            update = true;
        }
        pDb->setTransactionSuccessful();
        return update;
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "SQLite error while updateLastAccessedSec(): %s", e.getMessage().c_str());
        throw;
    }
}

bool HttpCacheDatabase::deleteDatabaseFile()
{
    Poco::FastMutex::ScopedLock lock(m_mutex);
    return FileUtil::removeFileIfPresent(Poco::File(m_pOpenHelper->getDatabasePath().absolute().toString()));
}

void HttpCacheDatabase::enumerate(HttpCacheEnumerationListener* pListener)
{
    // do not lock in enumerate because deleteMetadata might be called from onEnumerate callback.
    // when call enumerate, exclusive control is done by the caller.

    SqliteDatabase::Ptr pDb;
    try {
        pDb = m_pOpenHelper->getReadableDatabase();
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "enumerate() Unable to open database. Error: %s", e.getMessage().c_str());
        throw;
    }

    try {
        SqliteCursor::Ptr pCursor;

        {
            AutoSqliteDatabase autoSqliteDatabase(pDb);

            std::vector columns;
            columns.push_back(HttpInternalConstants::Database::Key::CacheKey);
            columns.push_back(HttpInternalConstants::Database::Key::ResponseBodySize);

            // sort by LastAccessedSec.
            std::string orderBy = std::string(HttpInternalConstants::Database::Key::LastAccessedAtEpoch) + " ASC";
            pCursor = pDb->query(HttpInternalConstants::Database::TableName, &columns, NULL, NULL, NULL,
                    NULL, &orderBy, NULL);
        }
        AutoSqliteCursor autoSqliteCursor(pCursor);
        if (pCursor->moveToFirst()) {
            do {
                HttpCacheEnumerationListener::EnumerationParam param;
                param.m_key = pCursor->getString(0);
                param.m_responseBodySize = pCursor->getUnsignedLongLong(1);
                if (!pListener->onEnumerate(param)) {
                    EASYHTTPCPP_LOG_D(Tag, "enumerate: error occurred onEnumerate.");
                    // could not store it in cache, but continue processing because it is cache. 
                }
            } while (pCursor->moveToNext());
        }
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "SQLite error while enumerate(): %s", e.getMessage().c_str());
        throw;
    }
}

void HttpCacheDatabase::closeSqliteSession()
{
    m_pOpenHelper->closeSqliteSession();
}

HttpCacheDatabase::HttpCacheMetadataAll::HttpCacheMetadataAll() : m_lastAccessedAtEpoch(0)
{
}

void HttpCacheDatabase::HttpCacheMetadataAll::setLastAccessedAtEpoch(std::time_t lastAccessedAtEpoch)
{
    m_lastAccessedAtEpoch = lastAccessedAtEpoch;
}

std::time_t HttpCacheDatabase::HttpCacheMetadataAll::getLastAccessedAtEpoch() const
{
    return m_lastAccessedAtEpoch;
}

HttpCacheDatabase::HttpCacheMetadataAll::Ptr HttpCacheDatabase::getMetadataAll(const std::string& key)
{
    // do not use lock because this method is for test.

    SqliteDatabase::Ptr pDb;
    try {
        pDb = m_pOpenHelper->getReadableDatabase();
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "getMetadataAll() Unable to open database. Error: %s", e.getMessage().c_str());
        throw;
    }
    AutoSqliteDatabase autoSqliteDatabase(pDb);

    try {
        std::vector columns;
        columns.push_back(HttpInternalConstants::Database::Key::Url);
        columns.push_back(HttpInternalConstants::Database::Key::Method);
        columns.push_back(HttpInternalConstants::Database::Key::StatusCode);
        columns.push_back(HttpInternalConstants::Database::Key::StatusMessage);
        columns.push_back(HttpInternalConstants::Database::Key::ResponseHeaderJson);
        columns.push_back(HttpInternalConstants::Database::Key::ResponseBodySize);
        columns.push_back(HttpInternalConstants::Database::Key::SentRequestAtEpoch);
        columns.push_back(HttpInternalConstants::Database::Key::ReceivedResponseAtEpoch);
        columns.push_back(HttpInternalConstants::Database::Key::CreatedAtEpoch);
        columns.push_back(HttpInternalConstants::Database::Key::LastAccessedAtEpoch);

        std::string whereClause = std::string(HttpInternalConstants::Database::Key::CacheKey) + "=?";
        std::vector whereArgs;
        whereArgs.push_back(key);

        SqliteCursor::Ptr pCursor = pDb->query(HttpInternalConstants::Database::TableName, &columns, &whereClause,
                &whereArgs, NULL, NULL, NULL, NULL);
        AutoSqliteCursor autoSqliteCursor(pCursor);
        if (pCursor->moveToFirst()) {
            HttpCacheDatabase::HttpCacheMetadataAll::Ptr pHttpCacheMetadataAll =
                    new HttpCacheDatabase::HttpCacheMetadataAll();
            pHttpCacheMetadataAll->setKey(key);
            pHttpCacheMetadataAll->setUrl(pCursor->getString(0));
            pHttpCacheMetadataAll->setHttpMethod(static_cast (pCursor->getInt(1)));
            pHttpCacheMetadataAll->setStatusCode(pCursor->getInt(2));
            pHttpCacheMetadataAll->setStatusMessage(pCursor->getString(3));
            pHttpCacheMetadataAll->setResponseHeaders(HttpUtil::exchangeJsonStrToHeaders(pCursor->getString(4)));
            pHttpCacheMetadataAll->setResponseBodySize(pCursor->getUnsignedLongLong(5));
            pHttpCacheMetadataAll->setSentRequestAtEpoch(pCursor->getUnsignedLongLong(6));
            pHttpCacheMetadataAll->setReceivedResponseAtEpoch(pCursor->getUnsignedLongLong(7));
            pHttpCacheMetadataAll->setCreatedAtEpoch(pCursor->getUnsignedLongLong(8));
            pHttpCacheMetadataAll->setLastAccessedAtEpoch(pCursor->getUnsignedLongLong(9));
            return pHttpCacheMetadataAll;
        } else {
            EASYHTTPCPP_LOG_D(Tag, "getMetadataAll(): can not get row");
            return NULL;
        }
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "SQLite error while getMetadataAll(): %s", e.getMessage().c_str());
        throw;
    }
}

void HttpCacheDatabase::updateMetadataAll(const std::string& key,
        HttpCacheDatabase::HttpCacheMetadataAll::Ptr pHttpCacheMetadataAll)
{
    // do not use lock because this method is for test.

    SqliteDatabase::Ptr pDb;
    try {
        pDb = m_pOpenHelper->getWritableDatabase();
    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "updateMetadataAll() Unable to open database. Error: %s", e.getMessage().c_str());
        throw;
    }
    AutoSqliteDatabase autoSqliteDatabase(pDb);

    try {
        // always use transactions for speedy and reliable updates
        AutoSqliteTransaction autoSqliteTransaction(pDb);

        ContentValues values;
        values.put(HttpInternalConstants::Database::Key::CacheKey, pHttpCacheMetadataAll->getKey());
        values.put(HttpInternalConstants::Database::Key::Url, pHttpCacheMetadataAll->getUrl());
        values.put(HttpInternalConstants::Database::Key::Method, pHttpCacheMetadataAll->getHttpMethod());
        values.put(HttpInternalConstants::Database::Key::StatusCode, pHttpCacheMetadataAll->getStatusCode());
        values.put(HttpInternalConstants::Database::Key::StatusMessage, pHttpCacheMetadataAll->getStatusMessage());
        values.put(HttpInternalConstants::Database::Key::ResponseHeaderJson,
                HttpUtil::exchangeHeadersToJsonStr(pHttpCacheMetadataAll->getResponseHeaders()));
        values.put(HttpInternalConstants::Database::Key::ResponseBodySize,
                pHttpCacheMetadataAll->getResponseBodySize());
        values.put(HttpInternalConstants::Database::Key::SentRequestAtEpoch,
                pHttpCacheMetadataAll->getSentRequestAtEpoch());
        values.put(HttpInternalConstants::Database::Key::ReceivedResponseAtEpoch,
                pHttpCacheMetadataAll->getReceivedResponseAtEpoch());
        values.put(HttpInternalConstants::Database::Key::CreatedAtEpoch, pHttpCacheMetadataAll->getCreatedAtEpoch());
        values.put(HttpInternalConstants::Database::Key::LastAccessedAtEpoch,
                pHttpCacheMetadataAll->getLastAccessedAtEpoch());

        // do an INSERT, and if that INSERT fails because of a conflict,
        // delete the conflicting rows before INSERTing again
        pDb->replace(HttpInternalConstants::Database::TableName, values);
        EASYHTTPCPP_LOG_V(Tag, "New HttpCacheMetadata updateMetadataAll.");

        pDb->setTransactionSuccessful();

    } catch (const SqlException& e) {
        EASYHTTPCPP_LOG_D(Tag, "SQLite error while updateMetadataAll(): %s", e.getMessage().c_str());
        throw;
    }
}

void HttpCacheDatabase::dumpMetadata(HttpCacheMetadata::Ptr pHttpCacheMetadata)
{
    EASYHTTPCPP_LOG_D(Tag, "url = %s", pHttpCacheMetadata->getUrl().c_str());
    EASYHTTPCPP_LOG_D(Tag, "method = %s", HttpUtil::httpMethodToString(pHttpCacheMetadata->getHttpMethod()).c_str());
    EASYHTTPCPP_LOG_D(Tag, "code = %d", pHttpCacheMetadata->getStatusCode());
    EASYHTTPCPP_LOG_D(Tag, "message = %s", pHttpCacheMetadata->getStatusMessage().c_str());
    EASYHTTPCPP_LOG_D(Tag, "header = %s", HttpUtil::exchangeHeadersToJsonStr(
            pHttpCacheMetadata->getResponseHeaders()).c_str());
    EASYHTTPCPP_LOG_D(Tag, "body size = %zu", pHttpCacheMetadata->getResponseBodySize());
    Poco::Timestamp sentRequestTime = Poco::Timestamp::fromEpochTime(pHttpCacheMetadata->getSentRequestAtEpoch());
    EASYHTTPCPP_LOG_D(Tag, "sentRequestAtEpoch = %s", Poco::DateTimeFormatter::format(sentRequestTime,
            Poco::DateTimeFormat::HTTP_FORMAT).c_str());
    Poco::Timestamp receiveedResponseTime =
            Poco::Timestamp::fromEpochTime(pHttpCacheMetadata->getReceivedResponseAtEpoch());
    EASYHTTPCPP_LOG_D(Tag, "receivedResponseAtEpoch = %s", Poco::DateTimeFormatter::format(receiveedResponseTime,
            Poco::DateTimeFormat::HTTP_FORMAT).c_str());
    Poco::Timestamp createMetadataTime = Poco::Timestamp::fromEpochTime(pHttpCacheMetadata->getCreatedAtEpoch());
    EASYHTTPCPP_LOG_D(Tag, "createAtEpoch = %s", Poco::DateTimeFormatter::format(createMetadataTime,
            Poco::DateTimeFormat::HTTP_FORMAT).c_str());
}

} /* namespace easyhttpcpp */

Web Proxy Viewer  |  New URL  |  Original Page