summaryrefslogtreecommitdiff
path: root/src/httpaction.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/httpaction.cc')
-rw-r--r--src/httpaction.cc325
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