CppServer  1.0.4.0
C++ Server Library
http_server.cpp

HTTP server example

#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;
};
class HTTPCacheSession : public CppServer::HTTP::HTTPSession
{
public:
protected:
void onReceivedRequest(const CppServer::HTTP::HTTPRequest& request) override
{
// Show HTTP request content
std::cout << std::endl << request;
// Process HTTP request methods
if (request.method() == "HEAD")
SendResponseAsync(response().MakeHeadResponse());
else if (request.method() == "GET")
{
std::string key(request.url());
std::string value;
// Decode the key value
key = CppCommon::Encoding::URLDecode(key);
CppCommon::StringUtils::ReplaceFirst(key, "/api/cache", "");
CppCommon::StringUtils::ReplaceFirst(key, "?key=", "");
if (key.empty())
{
// Response with all cache values
SendResponseAsync(response().MakeGetResponse(Cache::GetInstance().GetAllCache(), "application/json; charset=UTF-8"));
}
// Get the cache value by the given key
else if (Cache::GetInstance().GetCacheValue(key, value))
{
// Response with the cache value
SendResponseAsync(response().MakeGetResponse(value));
}
else
SendResponseAsync(response().MakeErrorResponse(404, "Required cache value was not found for the key: " + key));
}
else if ((request.method() == "POST") || (request.method() == "PUT"))
{
std::string key(request.url());
std::string value(request.body());
// Decode the key value
key = CppCommon::Encoding::URLDecode(key);
CppCommon::StringUtils::ReplaceFirst(key, "/api/cache", "");
CppCommon::StringUtils::ReplaceFirst(key, "?key=", "");
// Put the cache value
Cache::GetInstance().PutCacheValue(key, value);
// Response with the cache value
SendResponseAsync(response().MakeOKResponse());
}
else if (request.method() == "DELETE")
{
std::string key(request.url());
std::string value;
// Decode the key value
key = CppCommon::Encoding::URLDecode(key);
CppCommon::StringUtils::ReplaceFirst(key, "/api/cache", "");
CppCommon::StringUtils::ReplaceFirst(key, "?key=", "");
// Delete the cache value
if (Cache::GetInstance().DeleteCacheValue(key, value))
{
// Response with the cache value
SendResponseAsync(response().MakeGetResponse(value));
}
else
SendResponseAsync(response().MakeErrorResponse(404, "Deleted cache value was not found for the key: " + key));
}
else if (request.method() == "OPTIONS")
SendResponseAsync(response().MakeOptionsResponse());
else if (request.method() == "TRACE")
SendResponseAsync(response().MakeTraceResponse(request.cache()));
else
SendResponseAsync(response().MakeErrorResponse("Unsupported HTTP method: " + std::string(request.method())));
}
void onReceivedRequestError(const CppServer::HTTP::HTTPRequest& request, const std::string& error) override
{
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;
}
};
class HTTPCacheServer : public CppServer::HTTP::HTTPServer
{
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)
{
// HTTP server port
int port = 8080;
if (argc > 1)
port = std::atoi(argv[1]);
// HTTP server content path
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;
// Create a new Asio service
auto service = std::make_shared<AsioService>();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new HTTP server
auto server = std::make_shared<HTTPCacheServer>(service, port);
server->AddStaticContent(www, "/api");
// Start the server
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;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Restart the server
if (line == "!")
{
std::cout << "Server restarting...";
server->Restart();
std::cout << "Done!" << std::endl;
continue;
}
}
// Stop the server
std::cout << "Server stopping...";
server->Stop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
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.
Definition: tcp_server.h:207
virtual void onError(int error, const std::string &category, const std::string &message)
Handle error notification.
Definition: tcp_session.h:232
const std::string & cache() const noexcept
Get the HTTP request cache content.
Definition: http_request.h:77
std::string_view method() const noexcept
Get the HTTP request method.
Definition: http_request.h:58
std::string_view url() const noexcept
Get the HTTP request URL.
Definition: http_request.h:60
std::string_view body() const noexcept
Get the HTTP request body.
Definition: http_request.h:72
std::shared_ptr< Asio::TCPSession > CreateSession(const std::shared_ptr< Asio::TCPServer > &server) override
Definition: http_server.h:64
HTTPServer(const HTTPServer &)=delete
HTTPSession(const std::shared_ptr< HTTPServer > &server)
HTTPResponse & response() noexcept
Get the HTTP response.
Definition: http_session.h:45
virtual void onReceivedRequestError(const HTTPRequest &request, const std::string &error)
Handle HTTP request error notification.
Definition: http_session.h:173
bool SendResponseAsync()
Send the current HTTP response (asynchronous)
Definition: http_session.h:108
virtual void onReceivedRequest(const HTTPRequest &request)
Handle HTTP request received notification.
Definition: http_session.h:150
HTTP server definition.