-
-
Save caiorss/b915ecb586be56802bf221a24936a654 to your computer and use it in GitHub Desktop.
Chain-of-responsibility design pattern in C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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