diff options
Diffstat (limited to 'src/httpaction.cc')
| -rw-r--r-- | src/httpaction.cc | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/httpaction.cc b/src/httpaction.cc new file mode 100644 index 0000000..d24a652 --- /dev/null +++ b/src/httpaction.cc @@ -0,0 +1,325 @@ +// +// Created by luqiu on 2018-5-17. +// + +#include <cassert> +#include <vector> +#include <regex> + +#include "httpaction.h" +#include "log.h" +#include "http.h" +#include "util.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// HTTP白名单,不进行任何处理 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class HttpActionBypass : public HttpAction +{ +public: + void Construct(const std::string &str_service_define) override + {} + void OnRequestHeader(HttpSession *session) override + {} + void OnRequestBody(HttpSession *session) override + {} + void OnResponseHeader(HttpSession *session) override + {} + void OnResponseBody(HttpSession *session) override + {} +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// HTTP重定向 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class HttpActionRedirect : public HttpAction +{ +public: + void Construct(const std::string &str_service_define) override; + void OnRequestHeader(HttpSession *session) override + { return do_redirect_action(session); } + void OnRequestBody(HttpSession *session) override + { return do_redirect_action(session); } + void OnResponseHeader(HttpSession *session) override {}; + void OnResponseBody(HttpSession *session) override {}; + +private: + unsigned int resp_code_{500}; + std::string resp_location_{""}; + std::string resp_content_{""}; + + void do_redirect_action(HttpSession *session); +}; + +void HttpActionRedirect::Construct(const std::string &str_kv) +{ + std::vector<std::string> __split_token; + tokenize(str_kv, __split_token, ";", true); + + if (__split_token.size() != 2) + { + throw std::invalid_argument(string_format( + "Not enough tokens: %s, need two tokens.", str_kv.c_str())); + } + + for (const auto &kv_iterate : __split_token) + { + std::vector<std::string> __kv_tokens; + tokenize(kv_iterate, __kv_tokens, "=", false); + + if (__kv_tokens.size() != 2) + { + throw std::invalid_argument(string_format( + "Token %s must be conposed by key and value", kv_iterate.c_str())); + } + + const std::string &__str_key = __kv_tokens[0]; + const std::string &__str_value = __kv_tokens[1]; + + if (__str_key == "code") + { + resp_code_ = static_cast<unsigned int>(std::stoul(__str_value)); + continue; + } + + if (__str_key == "url") + { + resp_location_ = __str_value; + continue; + } + + assert(0); + } + +} + +void HttpActionRedirect::do_redirect_action(HttpSession *session) +{ + /* 创建新的HttpResponse */ + auto http_response = HttpResponseFactory(1, 1); + + /* 构建Redirect Response */ + http_response->ResponseCode(resp_code_); + http_response->HeaderValue("Location", resp_location_); + http_response->Construct(); + + /* 丢弃当前的HTTP Request,替换该请求对应的应答 */ + session->request().Forward(false); + session->response(std::move(http_response)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// HTTP连接阻断 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class HttpActionBlock : public HttpAction +{ +public: + void Construct(const std::string &str_service_define) override {} + void OnRequestHeader(HttpSession *session) override { do_block_action(session); } + void OnRequestBody(HttpSession *session) override {do_block_action(session);}; + void OnResponseHeader(HttpSession *session) override {do_block_action(session);}; + void OnResponseBody(HttpSession *session) override {do_block_action(session);}; + +private: + void do_block_action(HttpSession * session); +}; + +void HttpActionBlock::do_block_action(HttpSession *session) +{ + auto & connection = session->connection(); + connection.Close(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// HTTP内容编辑功能实现 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class HttpActionEdit : public HttpAction +{ +public: + void Construct(const std::string &str_kv) override; + void OnRequestHeader(HttpSession *session) override; + void OnRequestBody(HttpSession *session) override; + void OnResponseHeader(HttpSession *session) override; + void OnResponseBody(HttpSession *session) override; + +private: + enum edit_zone + { + kZoneRequestHeader, + kZoneRequestBody, + kZoneResponseHeader, + kZoneResponseBody, + kZoneMax + }; + + struct edit_rule + { + std::regex regex; + std::string fmt; + }; + + /* Edit Zone to string, define in maat service define */ + static std::array<std::string, kZoneMax> map_edit_zone_to_str; + std::array<std::vector<struct edit_rule>, kZoneMax> edit_rules_; +}; + +std::array<std::string, HttpActionEdit::kZoneMax> HttpActionEdit::map_edit_zone_to_str = + { + "http_req_header", + "http_req_body", + "http_resp_header", + "http_resp_body" + }; + +void HttpActionEdit::Construct(const std::string &str_kv) +{ + std::vector<std::string> __split_token; + tokenize(str_kv, __split_token, ";", true); + + if (__split_token.size() % 2 != 0) + { + throw std::invalid_argument(string_format( + "Invalid edit rule: %s, the count of tokens must be even numbers.", str_kv.c_str())); + } + + for (auto __iterate_zone = __split_token.cbegin(), __iterate_regex = __split_token.cbegin() + 1; + __iterate_zone != __split_token.cend(); __iterate_zone += 2, __iterate_regex + 2) + { + std::vector<std::string> __kv_tokens_zone; + std::vector<std::string> __kv_tokens_regex; + + tokenize(*__iterate_zone, __kv_tokens_zone, "="); + tokenize(*__iterate_regex, __kv_tokens_regex, "="); + + if (__kv_tokens_zone.size() != 2 || __kv_tokens_regex.size() != 2) + { + throw std::invalid_argument(string_format( + "Invalid edit rule: %s, the tokens must composed by key-value. ", str_kv.c_str())); + } + + if (__kv_tokens_zone[0] != "zone" || __kv_tokens_regex[0] != "regex") + { + throw std::invalid_argument(string_format( + "Invalid edit rule: %s, the tokens' key must be 'zone' or 'regex'", str_kv.c_str())); + } + + const std::string &__kv_zone = __kv_tokens_zone[1]; + const std::string &__kv_regex = __kv_tokens_regex[1]; + + /* 查找strZone对应的Zone数值 */ + auto zone_iterate = std::find(map_edit_zone_to_str.cbegin(), map_edit_zone_to_str.cend(), __kv_zone); + if (zone_iterate == map_edit_zone_to_str.cend()) + { + throw std::invalid_argument("Invalid edit rule: %s, illegal zone. "); + } + + auto zone_id = static_cast<edit_zone>(zone_iterate - map_edit_zone_to_str.cbegin()); + auto &zone_rule_ref = edit_rules_[zone_id]; + + std::vector<std::string> __token_regex; + + /* 按'/'拆分正则串,判断转义字符,若/前面是\,则不认为该字符为拆分字符 */ + tokenize(__kv_regex, __token_regex, "/", false, [](const std::string &str, std::string::size_type pos) + { + return (pos == 0 || str[pos - 1] != '\\'); + }); + + if (__token_regex.size() != 3) + { + throw std::invalid_argument(string_format( + "Invalid edit rule: %s, the regex must composed by two args.", str_kv.c_str())); + } + + const std::string &__kv_regex_base = __token_regex[1]; + const std::string &__kv_regex_fmt = __token_regex[2]; + + try + { + struct edit_rule __tmp_edit_rule = {std::regex(__kv_regex_base), __kv_regex_fmt}; + zone_rule_ref.push_back(std::move(__tmp_edit_rule)); + } + catch (std::regex_error &e) + { + throw std::invalid_argument(string_format( + "Invalid edit rule: %s, illegal regex expr, %d", str_kv.c_str(), e.code())); + } + } + + return; +} + +void HttpActionEdit::OnRequestHeader(HttpSession *session) +{ + /* 当前请求为transport时,无法替换,因为传输内容已经转发完毕了 */ + auto &request_ctx = session->request(); + + /* 扫描规则库,逐条执行 */ + const auto &edit_rule = edit_rules_[kZoneRequestHeader]; + + for (const auto &edit_rule_iter : edit_rule) + { + /* 正则表达式,确定要替换的内容 */ + const auto __edit_regex = edit_rule_iter.regex; + /* 替换表达式,替换方法 */ + const auto __edit_fmt = edit_rule_iter.fmt; + } + + return; +} + +void HttpActionEdit::OnRequestBody(HttpSession *session) +{ + const auto &edit_rule = &edit_rules_[kZoneRequestBody]; + + return; +} + +void HttpActionEdit::OnResponseHeader(HttpSession *session) +{ + +} + +void HttpActionEdit::OnResponseBody(HttpSession *session) +{ + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// 工厂函数 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +std::shared_ptr<HttpAction> HttpActionFactory(enum HttpActionType type, std::string str_service_define) +{ + std::shared_ptr<HttpAction> __http_action_object; + + switch (type) + { + case kActionBypass: + { + __http_action_object = std::make_shared<HttpActionBypass>(); + break; + } + case kActionEdit: + { + __http_action_object = std::make_shared<HttpActionEdit>(); + break; + } + case kActionBlock: + { + __http_action_object = std::make_shared<HttpActionBlock>(); + break; + } + case kActionRedirect: + { + __http_action_object = std::make_shared<HttpActionRedirect>(); + break; + } + default: assert(0); + } + + __http_action_object->Construct(str_service_define); + return std::move(__http_action_object); +}
\ No newline at end of file |
