#include "asio_service.h"
#include "string/string_utils.h"
#include "utility/singleton.h"
#include <iostream>
#include <map>
#include <mutex>
class Cache : public CppCommon::Singleton<Cache>
{
friend CppCommon::Singleton<Cache>;
public:
std::string GetAllCache()
{
std::scoped_lock locker(_cache_lock);
std::string result;
result += "[\n";
for (const auto& item : _cache)
{
result += " {\n";
result += " \"key\": \"" + item.first + "\",\n";
result += " \"value\": \"" + item.second + "\",\n";
result += " },\n";
}
result += "]\n";
return result;
}
bool GetCacheValue(std::string_view key, std::string& value)
{
std::scoped_lock locker(_cache_lock);
auto it = _cache.find(key);
if (it != _cache.end())
{
value = it->second;
return true;
}
else
return false;
}
void PutCacheValue(std::string_view key, std::string_view value)
{
std::scoped_lock locker(_cache_lock);
auto it = _cache.emplace(key, value);
if (!it.second)
it.first->second = value;
}
bool DeleteCacheValue(std::string_view key, std::string& value)
{
std::scoped_lock locker(_cache_lock);
auto it = _cache.find(key);
if (it != _cache.end())
{
value = it->second;
_cache.erase(it);
return true;
}
else
return false;
}
private:
std::mutex _cache_lock;
std::map<std::string, std::string, std::less<>> _cache;
};
{
public:
protected:
{
std::cout << std::endl << request;
if (request.
method() ==
"HEAD")
else if (request.
method() ==
"GET")
{
std::string key(request.
url());
std::string value;
key = CppCommon::Encoding::URLDecode(key);
CppCommon::StringUtils::ReplaceFirst(key, "/api/cache", "");
CppCommon::StringUtils::ReplaceFirst(key, "?key=", "");
if (key.empty())
{
}
else if (Cache::GetInstance().GetCacheValue(key, value))
{
}
else
}
else if ((request.
method() ==
"POST") || (request.
method() ==
"PUT"))
{
std::string key(request.
url());
std::string value(request.
body());
key = CppCommon::Encoding::URLDecode(key);
CppCommon::StringUtils::ReplaceFirst(key, "/api/cache", "");
CppCommon::StringUtils::ReplaceFirst(key, "?key=", "");
Cache::GetInstance().PutCacheValue(key, value);
}
else if (request.
method() ==
"DELETE")
{
std::string key(request.
url());
std::string value;
key = CppCommon::Encoding::URLDecode(key);
CppCommon::StringUtils::ReplaceFirst(key, "/api/cache", "");
CppCommon::StringUtils::ReplaceFirst(key, "?key=", "");
if (Cache::GetInstance().DeleteCacheValue(key, value))
{
}
else
}
else if (request.
method() ==
"OPTIONS")
else if (request.
method() ==
"TRACE")
else
}
{
std::cout << "Request error: " << error << std::endl;
}
void onError(
int error,
const std::string& category,
const std::string& message)
override
{
std::cout << "HTTP session caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
{
public:
protected:
std::shared_ptr<CppServer::Asio::TCPSession>
CreateSession(
const std::shared_ptr<CppServer::Asio::TCPServer>& server)
override
{
return std::make_shared<HTTPCacheSession>(std::dynamic_pointer_cast<CppServer::HTTP::HTTPServer>(server));
}
protected:
void onError(
int error,
const std::string& category,
const std::string& message)
override
{
std::cout << "HTTP server caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
int main(int argc, char** argv)
{
int port = 8080;
if (argc > 1)
port = std::atoi(argv[1]);
std::string www = "../www/api";
if (argc > 2)
www = argv[2];
std::cout << "HTTP server port: " << port << std::endl;
std::cout << "HTTP server static content path: " << www << std::endl;
std::cout << "HTTP server website: " << "http://localhost:" << port << "/api/index.html" << std::endl;
std::cout << std::endl;
auto service = std::make_shared<AsioService>();
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
auto server = std::make_shared<HTTPCacheServer>(service, port);
server->AddStaticContent(www, "/api");
std::cout << "Server starting...";
server->Start();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the server or '!' to restart the server..." << std::endl;
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
if (line == "!")
{
std::cout << "Server restarting...";
server->Restart();
std::cout << "Done!" << std::endl;
continue;
}
}
std::cout << "Server stopping...";
server->Stop();
std::cout << "Done!" << std::endl;
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
virtual void onError(int error, const std::string &category, const std::string &message)
Handle error notification.
virtual void onError(int error, const std::string &category, const std::string &message)
Handle error notification.
const std::string & cache() const noexcept
Get the HTTP request cache content.
std::string_view method() const noexcept
Get the HTTP request method.
std::string_view url() const noexcept
Get the HTTP request URL.
std::string_view body() const noexcept
Get the HTTP request body.
std::shared_ptr< Asio::TCPSession > CreateSession(const std::shared_ptr< Asio::TCPServer > &server) override
HTTPServer(const HTTPServer &)=delete
HTTPSession(const std::shared_ptr< HTTPServer > &server)
HTTPResponse & response() noexcept
Get the HTTP response.
virtual void onReceivedRequestError(const HTTPRequest &request, const std::string &error)
Handle HTTP request error notification.
bool SendResponseAsync()
Send the current HTTP response (asynchronous)
virtual void onReceivedRequest(const HTTPRequest &request)
Handle HTTP request received notification.