Skip to content

Instantly share code, notes, and snippets.

@caiorss
Created August 31, 2020 00:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caiorss/b915ecb586be56802bf221a24936a654 to your computer and use it in GitHub Desktop.
Save caiorss/b915ecb586be56802bf221a24936a654 to your computer and use it in GitHub Desktop.
Chain-of-responsibility design pattern in C++
#include <iostream>
#include <string>
enum class HttpMethod {
GET, POST, DELETE, PUT, HEAD, PATCH, UKNOWN_METHOD
};
struct HttpRequest {
std::string path = "/";
HttpMethod method = HttpMethod::GET;
std::string payload = "";
// Missing - http headers ---- //
bool isGET() const { return method == HttpMethod::GET; }
bool isPOST() const { return method == HttpMethod::POST; }
bool isDELETE() const { return method == HttpMethod::DELETE; }
};
/** Process server Http GET request and returns a response. */
void dispatch_request(HttpRequest const& req)
{
// Http route: http://www.mysite.com/
if(req.isGET() && ( req.path == "/" || req.path == ""))
{
std::printf(" => Serve index page, web site homepage \n");
// Http route: http://www.mysite.com/download
} else if( req.isGET() && req.path == "/download" )
{
std::printf(" => Serve http://www.mysite.com/dowload page. ");
// Http route: http://www.mysite.com/upload
} else if( req.isPOST() && req.path == "/upload")
{
std::printf(" => Upload document to web server \n");
// Http route: http://www.mysite.com/<error-failure>
} else {
std::printf(" => Error: 404 - Not Found resource: '%s' \n", req.path.c_str());
}
}
int main(int argc, char** argv)
{
auto req1 = HttpRequest{ "/", HttpMethod::GET, "" };
dispatch_request(req1);
auto req2 = HttpRequest{ "/error/resource", HttpMethod::GET, "" };
dispatch_request(req2);
auto req3 = HttpRequest{ "/upload", HttpMethod::POST, "file data" };
dispatch_request(req3);
return 0;
}
#include <iostream>
#include <string>
enum class HttpMethod {
GET, POST, DELETE, PUT, HEAD
};
/** Request object - encapsulates a http request */
struct HttpRequest {
std::string path = "/";
HttpMethod method = HttpMethod::GET;
std::string payload = "";
// Missing - http headers implementation ---- //
bool isGET() const { return method == HttpMethod::GET; }
bool isPOST() const { return method == HttpMethod::POST; }
bool isDELETE() const { return method == HttpMethod::DELETE; }
};
/** Interface class for generic node of chain of responsibility */
class IHttpHandler {
public:
virtual void handle(const HttpRequest& req) = 0;
virtual void set_next(IHttpHandler* next) = 0;
virtual ~IHttpHandler() = default;
};
class BasicHttpHandler: public IHttpHandler {
protected:
/** Note: the keyword 'protected' allows derived classes
* (aka subclasses) to access the 'm_next' pointer. But
* this keyword forbids any external code from accessing
* the pointer m_next.
*/
IHttpHandler* m_next = nullptr;
public:
BasicHttpHandler(IHttpHandler* next = nullptr)
: m_next(next){ }
virtual ~BasicHttpHandler() {
delete m_next;
m_next = nullptr;
}
/** Set next request in the chain */
void set_next(IHttpHandler* next)
{
m_next = next;
}
};
class HttpGetHandler: public BasicHttpHandler {
std::string m_path;
public:
HttpGetHandler(std::string path): m_path(path){ }
void handle(const HttpRequest& req) override
{
// Checks whether this handler object can process the request.
if(req.method == HttpMethod::GET && m_path == req.path) {
std::printf( " [RESPONSE] <HttpGetHandler> Return Html and http response with "
"status code 200 for resource: %s \n", m_path.c_str());
} else {
// If the request cannot be handle, this hanlder forwards the request to
// the next handler in the chain.
m_next->handle(req);
}
}
};
class HttpUploadHandler: public BasicHttpHandler {
void handle(const HttpRequest& req) override
{
if( req.method == HttpMethod::POST && req.path == "/upload" )
std::printf( " [RESPONSE] <HttpUploadHandler> Uploading data: '%s' \n"
, req.payload.c_str());
else
m_next->handle(req);
}
};
class HttpError404Handler: public BasicHttpHandler {
void handle(const HttpRequest& req) override
{
std::printf(" [RESPONSE] <HttpError404Handler>"
" Error 404 Not found resource: %s \n", req.path.c_str());
}
};
struct HttpServer
{
// First handler in the chain
IHttpHandler* m_hnd;
HttpServer() {
IHttpHandler* hnd_index = new HttpGetHandler("/");
IHttpHandler* hnd_download = new HttpGetHandler("/download");
hnd_index->set_next(hnd_download);
IHttpHandler* hnd_upload = new HttpUploadHandler();
hnd_download->set_next(hnd_upload);
IHttpHandler* hnd_error = new HttpError404Handler();
hnd_upload->set_next(hnd_error);
m_hnd = hnd_index;
}
~HttpServer() {
delete m_hnd;
m_hnd = nullptr;
}
void dispatch_request(const HttpRequest& req)
{
std::printf("\n [TRACE] Process request => path = %s \n", req.path.c_str());
m_hnd->handle(req);
std::printf(" ---------------------------------------\n");
}
};
int main(int argc, char** argv)
{
HttpServer server;
auto req1 = HttpRequest{ "/", HttpMethod::GET, "" };
server.dispatch_request(req1);
auto req2 = HttpRequest{ "/error/resource", HttpMethod::GET, "" };
server.dispatch_request(req2);
auto req3 = HttpRequest{ "/upload", HttpMethod::POST, "file data" };
server.dispatch_request(req3);
return 0;
}
#include <iostream>
#include <string>
#include <memory>
enum class HttpMethod {
GET, POST, DELETE, PUT, HEAD
};
/** Request object - encapsulates a http request */
struct HttpRequest {
std::string path = "/";
HttpMethod method = HttpMethod::GET;
std::string payload = "";
// Missing - http headers implementation ---- //
bool isGET() const { return method == HttpMethod::GET; }
bool isPOST() const { return method == HttpMethod::POST; }
bool isDELETE() const { return method == HttpMethod::DELETE; }
};
/** Interface class for generic node of chain of responsibility */
class IHttpHandler {
public:
/** Predicate that checks whether request can be handled */
virtual bool can_handle(const HttpRequest& req) = 0;
/** Process (aka handle) request */
virtual void do_handle(const HttpRequest& req) = 0;
virtual void handle(const HttpRequest& req) = 0;
~IHttpHandler() = default;
};
class BasicHttpHandler: public IHttpHandler {
std::shared_ptr<IHttpHandler> m_next;
public:
BasicHttpHandler(std::shared_ptr<IHttpHandler> next = nullptr)
: m_next(next){ }
virtual ~BasicHttpHandler() = default;
void set_next(std::shared_ptr<IHttpHandler> next)
{
m_next = next;
}
/** Note: The keyword 'final' causes compile-time error if this
* method is overriden by any class derived from this one.
*/
void handle(const HttpRequest& req) final
{
if(this->can_handle(req))
this->do_handle(req);
else if(m_next != nullptr)
m_next->handle(req);
else
throw std::runtime_error(" There is no handle for this request.");
}
};
class HttpGetHandler: public BasicHttpHandler {
std::string m_path;
public:
HttpGetHandler(std::string path): m_path(path){ }
bool can_handle(const HttpRequest& req) override
{
return req.method == HttpMethod::GET && m_path == req.path;
}
void do_handle(const HttpRequest& req) override
{
std::printf( " [RESPONSE] <HttpGetHandler> Return Html and http response with "
"status code 200 for resource: %s \n", m_path.c_str());
}
};
class HttpUploadHandler: public BasicHttpHandler {
bool can_handle(const HttpRequest& req) override
{
return req.method == HttpMethod::POST && req.path == "/upload";
}
void do_handle(const HttpRequest& req) override
{
std::printf( " [RESPONSE] <HttpUploadHandler> Uploading data: '%s' \n"
, req.payload.c_str());
}
};
class HttpError404Handler: public BasicHttpHandler {
bool can_handle(const HttpRequest& req) override
{
return true;
}
void do_handle(const HttpRequest& req) override
{
std::printf(" [RESPONSE] <HttpError404Handler>"
" Error 404 Not found resource: %s \n", req.path.c_str());
}
};
struct HttpServer
{
std::shared_ptr<IHttpHandler> m_hnd;
HttpServer() {
auto hnd_index = std::make_shared<HttpGetHandler>("/");
auto hnd_download = std::make_shared<HttpGetHandler>("/download");
hnd_index->set_next(hnd_download);
auto hnd_upload = std::make_shared<HttpUploadHandler>();
hnd_download->set_next(hnd_upload);
auto hnd_error = std::make_shared<HttpError404Handler>();
hnd_upload->set_next(hnd_error);
m_hnd = hnd_index;
}
void dispatch_request(const HttpRequest& req)
{
std::printf("\n [TRACE] Process request => path = %s \n", req.path.c_str());
m_hnd->handle(req);
std::printf(" ---------------------------------------\n");
}
};
int main(int argc, char** argv)
{
HttpServer server;
auto req1 = HttpRequest{ "/", HttpMethod::GET, "" };
server.dispatch_request(req1);
auto req2 = HttpRequest{ "/error/resource", HttpMethod::GET, "" };
server.dispatch_request(req2);
auto req3 = HttpRequest{ "/upload", HttpMethod::POST, "file data" };
server.dispatch_request(req3);
return 0;
}
#include <iostream>
#include <string>
#include <vector>
#include <memory>
enum class HttpMethod {
GET, POST, DELETE, PUT, HEAD
};
/** Request object - encapsulates a http request */
struct HttpRequest {
std::string path = "/";
HttpMethod method = HttpMethod::GET;
std::string payload = "";
// Missing - http headers implementation ---- //
bool isGET() const { return method == HttpMethod::GET; }
bool isPOST() const { return method == HttpMethod::POST; }
bool isDELETE() const { return method == HttpMethod::DELETE; }
};
/** Interface class for generic node of chain of responsibility */
class IHttpHandler {
public:
/** Predicate that checks whether request can be handled */
virtual bool can_handle(const HttpRequest& req) = 0;
/** Process (aka handle) http request and returns http response. */
virtual void handle(const HttpRequest& req) = 0;
~IHttpHandler() = default;
};
class HttpGetHandler: public IHttpHandler {
std::string m_path;
public:
HttpGetHandler(std::string path): m_path(path){ }
bool can_handle(const HttpRequest& req) override
{
return req.method == HttpMethod::GET && m_path == req.path;
}
void handle(const HttpRequest& req) override
{
std::printf( " [RESPONSE] <HttpGetHandler> Return Html and http response with "
"status code 200 for resource: %s \n", m_path.c_str());
}
};
/* This class allows creating http handlers (transaction handlers)
* using C++ lambdas.
*/
template<typename predicate_t, typename action_t>
class AdapterHandler: public IHttpHandler
{
predicate_t m_predicate;
action_t m_action;
public:
AdapterHandler(predicate_t predicate, action_t action)
: m_predicate(predicate), m_action(action) { }
bool can_handle(const HttpRequest& req) override
{
return m_predicate(req);
}
void handle(const HttpRequest& req) override
{
return m_action(req);
}
};
struct HttpServer
{
std::vector<std::shared_ptr<IHttpHandler>> m_handlers;
HttpServer() {
addHandler(std::make_shared<HttpGetHandler>("/"));
addHandler(std::make_shared<HttpGetHandler>("/download"));
addLambdaHandler(
[](const HttpRequest& req)
{
return req.method == HttpMethod::POST && req.path == "/upload";
}
,[](const HttpRequest& req)
{
std::printf( " [RESPONSE] <HttpUploadHandler> Uploading data: '%s' \n"
, req.payload.c_str());
}
);
addLambdaHandler(
[](const HttpRequest& req){ return true; }
,[](const HttpRequest& req){
std::printf(" [RESPONSE] <HttpError404Handler>"
" Error 404 Not found resource: %s \n", req.path.c_str());
}
);
}
void addHandler(std::shared_ptr<IHttpHandler> hnd)
{
m_handlers.push_back(hnd);
}
template<typename predicate_t, typename action_t>
void addLambdaHandler(predicate_t pred, action_t action)
{
using T = AdapterHandler<predicate_t, action_t>;
auto hnd = std::make_shared<T>(pred, action);
m_handlers.push_back( hnd );
}
void dispatch_request(const HttpRequest& req)
{
std::printf("\n [TRACE] Process request => path = %s \n", req.path.c_str());
for(const auto& hnd: m_handlers)
if(hnd->can_handle(req)) {
hnd->handle(req);
return;
}
std::printf(" ---------------------------------------\n");
}
};
int main(int argc, char** argv)
{
HttpServer server;
auto req1 = HttpRequest{ "/", HttpMethod::GET, "" };
server.dispatch_request(req1);
auto req2 = HttpRequest{ "/error/resource", HttpMethod::GET, "" };
server.dispatch_request(req2);
auto req3 = HttpRequest{ "/upload", HttpMethod::POST, "file data" };
server.dispatch_request(req3);
return 0;
}
cmake_minimum_required(VERSION 3.9)
project(Simple_Cmake_Project)
#========== Global Configurations =============#
#----------------------------------------------#
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_VERBOSE_MAKEFILE ON)
#========== Targets Configurations ============#
add_executable(chain-of-responsibility1 chain-of-responsibility1.cpp)
add_executable(chain-of-responsibility2 chain-of-responsibility2.cpp)
add_executable(chain-of-responsibility3 chain-of-responsibility3.cpp)
add_executable(chain-of-responsibility4 chain-of-responsibility4.cpp)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment