diff options
| author | Lu <[email protected]> | 2018-07-18 20:07:55 +0800 |
|---|---|---|
| committer | Lu <[email protected]> | 2018-07-18 20:07:55 +0800 |
| commit | 961158e968761cc615ec058e77ece1711c9cbd01 (patch) | |
| tree | a9f6346c6f3f12e83c42d333aa62f6cf42b2bedd | |
| parent | 992fdd27ed695f9285eff808e4bc184aeee52b89 (diff) | |
#2 基本实现HTTP应答侧内容替换功能,支持对HTTP应答头部和应答体的内容替换。
| -rw-r--r-- | src/http.h | 10 | ||||
| -rw-r--r-- | src/http1.cc | 95 | ||||
| -rw-r--r-- | src/httpaction.cc | 103 | ||||
| -rw-r--r-- | src/httpscan.cc | 11 |
4 files changed, 181 insertions, 38 deletions
@@ -329,11 +329,17 @@ public: const sockaddr * SockAddrDest() const override; private: + enum direction + { + kDirectionRequest, + kDirectionResponse + }; + using http_sessions_t = std::list<std::unique_ptr<HttpSession>>; http_sessions_t http_sessions_{}; - HttpSession & create_new_session(); - HttpSession & last_uncomplete_session(); + HttpSession * create_new_session(); + HttpSession * last_uncomplete_session(enum direction dir); void drop_last_session(); void drop_first_session(); diff --git a/src/http1.cc b/src/http1.cc index 868d367..6a06d15 100644 --- a/src/http1.cc +++ b/src/http1.cc @@ -545,7 +545,9 @@ public: void Body(std::vector<body_content_ptr_t> body) override { + assert(body_contents_.empty()); body_contents_.clear(); + for (auto & body_content_iter : body) { body_contents_.emplace_back(std::move(body_content_iter), true); @@ -557,7 +559,6 @@ private: short resp_version_major_{1}; short resp_version_minor_{1}; - bool parse_complete_{false}; bool forward_{true}; bool readonly_{false}; @@ -831,9 +832,29 @@ void Http1Response::Construct() /* 结尾行 */ evbuffer_add_printf(evbuf_ptr, "\r\n"); - /* TODO: 消息体的构建 */ + for(auto & body_segment : body_contents_) + { + if (body_segment.ptr == nullptr || !body_segment.is_complete) continue; + auto * body_segment_ptr = body_segment.ptr.get(); + + if (body_is_chunk) + { + evbuffer_add_printf(evbuf_ptr, "%lx\r\n", body_segment_ptr->size()); + evbuffer_add(evbuf_ptr, body_segment_ptr->data(), body_segment.ptr->size()); + evbuffer_add_printf(evbuf_ptr, "\r\n"); + } + else + { + evbuffer_add(evbuf_ptr, body_segment.ptr->data(), body_segment.ptr->size()); + } + } + evbuf_content_raw_ = std::move(evbuf_construct); - parse_complete_ = true; + section_state[kSectionHeader] = kStateComplete; + section_state[kSectionBody] = kStateComplete; + section_state[kSectionMessage] = kStateComplete; + + return; } evbuffer_unique_ptr_t Http1Response::StolenEvBuf() @@ -865,14 +886,14 @@ int Http1Connection::on_connection_read_request(pxy_conn_ctx_t * conn_ctx, pxy_c pxy_conn_desc_t * conn_other) { /* Get the last session */ - auto & http_session = last_uncomplete_session(); - auto & request = dynamic_cast<Http1Request &>(http_session.request()); + auto * http_session = last_uncomplete_session(kDirectionRequest); + assert(http_session != nullptr); + auto & request = dynamic_cast<Http1Request &>(http_session->request()); auto * downstream_evbuf = bufferevent_get_input(conn_this->bev); auto * upstream_evbuf = bufferevent_get_output(conn_other->bev); ssize_t forward_len = request.ConstructFromEvBuf(downstream_evbuf); - if (forward_len < 0) { LOG(DEBUG) << "ctx = " << conn_ctx << " " @@ -884,15 +905,17 @@ int Http1Connection::on_connection_read_request(pxy_conn_ctx_t * conn_ctx, pxy_c if (request.Complete(HttpRequest::kSectionHeader)) { - http_session.CallRequestHeaderCallback(); + LOG(DEBUG) << std::addressof(http_session) << "CallRequestHeaderCallback"; + http_session->CallRequestHeaderCallback(); } if (request.Complete(HttpRequest::kSectionBody) && request.Body() != nullptr) { - http_session.CallRequestBodyCallback(); + LOG(DEBUG) << std::addressof(http_session) << "CallRequestBodyCallback"; + http_session->CallRequestBodyCallback(); } - if (http_session.NeedToDrop()) + if (http_session->NeedToDrop()) { drop_last_session(); return 0; @@ -910,14 +933,14 @@ int Http1Connection::on_connection_read_request(pxy_conn_ctx_t * conn_ctx, pxy_c int Http1Connection::on_connection_read_response(pxy_conn_ctx_t * conn_ctx, pxy_conn_desc_t * conn_this, pxy_conn_desc_t * conn_other) { - auto & http_session = last_uncomplete_session(); - auto & response = dynamic_cast<Http1Response &>(http_session.response()); + auto * http_session = last_uncomplete_session(kDirectionResponse); + assert(http_session != nullptr); + auto & response = dynamic_cast<Http1Response &>(http_session->response()); auto * downstream_evbuf = bufferevent_get_input(conn_this->bev); auto * upstream_evbuf = bufferevent_get_output(conn_other->bev); ssize_t forward_len = response.ConstructFromEvBuf(downstream_evbuf); - if (forward_len < 0) { LOG(DEBUG) << conn_ctx << "ResponseConstructFromEvBuf Failed, connection turns to passthrough"; @@ -931,12 +954,14 @@ int Http1Connection::on_connection_read_response(pxy_conn_ctx_t * conn_ctx, pxy_ if (response.SectionState(response.kSectionHeader) == response.kStateComplete) { - http_session.CallRequestHeaderCallback(); + LOG(DEBUG) << std::addressof(http_session) << "CallResponseHeaderCallback"; + http_session->CallResponseHeaderCallback(); } if (response.SectionState(response.kSectionBody) == response.kStateComplete) { - http_session.CallResponseBodyCallback(); + LOG(DEBUG) << std::addressof(http_session) << "CallResponseBodyCallback"; + http_session->CallResponseBodyCallback(); } /* 如果有任何一部分为Reading,说明该部分数据还为到来,暂时不转发 */ @@ -952,13 +977,13 @@ int Http1Connection::on_connection_read_response(pxy_conn_ctx_t * conn_ctx, pxy_ if (response.SectionState(response.kSectionMessage) == response.kStateComplete) { - drop_last_session(); + drop_first_session(); } return 0; } -HttpSession & Http1Connection::create_new_session() +HttpSession * Http1Connection::create_new_session() { /* Create new session, set a new request */ auto __http_session = std::unique_ptr<HttpSession>(new HttpSession(*this)); @@ -970,22 +995,40 @@ HttpSession & Http1Connection::create_new_session() /* Add to the last record */ http_sessions_.push_back(std::move(__http_session)); - return *http_sessions_.back(); + return http_sessions_.back().get(); } -HttpSession & Http1Connection::last_uncomplete_session() +HttpSession * Http1Connection::last_uncomplete_session(enum direction dir) { - if (http_sessions_.cbegin() == http_sessions_.cend()) - return create_new_session(); + if (dir == kDirectionRequest) + { + if (http_sessions_.empty()) + { + return create_new_session(); + } - /* 最后一个Session已经处理结束了,新建一个Session */ - auto & __session = http_sessions_.back(); + /* 看一下请求侧的状态,如果已经处理完了,说明使用了HttpPipeLine,新建一个Session */ + auto * __session = http_sessions_.back().get(); + if (__session->request().Complete(HttpRequest::kSecionMessage)) + return create_new_session(); - if (__session->request().Complete(HttpRequest::kSecionMessage)) - return create_new_session(); + /* 否则,返回最后一个没有完全处理结束的Session */ + return __session; + } + else if (dir == kDirectionResponse) + { + if (http_sessions_.empty()) + { + LOG(DEBUG) << "session queue is empty, no corresponding session for response."; + return nullptr; + } + + auto * __session = http_sessions_.front().get(); + return __session; + } - /* 否则,返回最后一个没有完全处理结束的Session */ - return *__session; + assert(0); + return nullptr; } void Http1Connection::drop_last_session() diff --git a/src/httpaction.cc b/src/httpaction.cc index 2cce21d..a4dd07e 100644 --- a/src/httpaction.cc +++ b/src/httpaction.cc @@ -405,16 +405,17 @@ private: /* Scan and Replace */ bool __scan_and_replace(enum edit_zone, std::string & raw); + bool __is_replaced{false}; }; std::array<std::string, HttpActionReplace::kZoneMax> HttpActionReplace::map_edit_zone_to_str = -{ - "http_req_uri", - "http_req_headers", - "http_req_body", - "http_resp_headers", - "http_resp_body" -}; + { + "http_req_uri", + "http_req_headers", + "http_req_body", + "http_resp_headers", + "http_resp_body" + }; void HttpActionReplace::Construct(const std::string & str_kv) { @@ -572,11 +573,99 @@ void HttpActionReplace::__on_request_body(HttpSession * session) void HttpActionReplace::__on_response_header(HttpSession * session) { + auto & response = session->response(); + + /* 对Headers替换 */ + response.Headers().ForEachHeader([this, &response] + (const std::string & str_field, const std::string str_value) -> bool + { + /* Field, Value组合成字符串,整体调用正则扫描 */ + std::string __combine_header = str_field + ":" + str_value; + + /* 没有命中,继续调用本函数处理后面的Headers */ + if (!__scan_and_replace(kZoneResponseHeader, __combine_header)) + return true; + + /* 替换标志 */ + __is_replaced = true; + + /* 命中,若替换后的长度为0,删除该头部 */ + if (__combine_header.length() == 0) + { + response.Headers().Remove(str_field); + return true; + } + + /* 按分号拆分成Field和Value */ + auto first_comma_pos = __combine_header.find_first_of(':'); + if (first_comma_pos == std::string::npos) + { + throw std::runtime_error("Invalid regex replacement for http headers, no comma found."); + } + + auto __replaced_field = __combine_header.substr(0, first_comma_pos); + auto __replaced_value = __combine_header.substr(first_comma_pos + 1); + + response.Headers().Set(__replaced_field, __replaced_value); + return true; + }); + return; } void HttpActionReplace::__on_response_body(HttpSession * session) { + auto & response = session->response(); + auto resp_bodys = response.StolenBody(); + + size_t total_body_bytes = 0; + std::vector<HttpResponse::body_content_ptr_t> body_replaced_segments; + + for (auto & body_raw_ptr : resp_bodys) + { + std::string str_body_raw = std::string(body_raw_ptr->begin(), body_raw_ptr->end()); + std::unique_ptr<std::vector<char>> body_replaced_ptr; + + if (__scan_and_replace(kZoneResponseBody, str_body_raw)) + { + body_replaced_ptr = std::make_unique<std::vector<char>>(); + body_replaced_ptr->insert(body_replaced_ptr->end(), str_body_raw.begin(), str_body_raw.end()); + __is_replaced = true; + } + else + { + body_replaced_ptr = std::move(body_raw_ptr); + } + + total_body_bytes += body_replaced_ptr->size(); + body_replaced_segments.push_back(std::move(body_replaced_ptr)); + } + + response.Body(std::move(body_replaced_segments)); + + /* 发生替换后,重新构造应答内容 */ + if (__is_replaced) + { + bool is_content_length_set = false; + + /* 原始头部是否有Content-Length字段 */ + response.cHeaders().ForEachValueOfHeader("Content-Length", [&is_content_length_set]( + const std::string _, const std::string __) + { + is_content_length_set = true; + return false; + }); + + /* 如果有,需要根据替换后的内容校正该数值 */ + if (is_content_length_set) + { + response.Headers().Set("Content-Length", std::to_string(total_body_bytes)); + } + + /* 构建应答内容 */ + response.Construct(); + } + return; } diff --git a/src/httpscan.cc b/src/httpscan.cc index 4f04067..5700669 100644 --- a/src/httpscan.cc +++ b/src/httpscan.cc @@ -236,7 +236,6 @@ void HttpScanSession::ScanResponseHeader(HttpSession * http_session_ctx) /* 扫描应答头部 */ auto scan_result = scan_headers(response.cHeaders(), httpscan_module_ref_.table_id_ctrl_http_res_hdr); - /* Hit */ if (scan_result == scan_result_t::kScanResultHit) { @@ -276,8 +275,6 @@ void HttpScanSession::ScanResponseBody(HttpSession * http_session_ctx) { return hit_scan_error(); } - - //CLOG(DEBUG, "HttpScanTrace") << hexdump("ContentBody", body_content_raw, body_content_length); } return; @@ -368,24 +365,32 @@ void HttpScanSession::hit_config_and_do_action(HttpSession * session) session->SetRequestHeaderCallback([action_object](HttpSession & session) { auto __action_object = action_object; + + LOG(DEBUG) << &session << "HttpAction" << "OnRequestHeader"; __action_object->OnRequestHeader(&session); }); session->SetRequestBodyCallback([action_object](HttpSession & session) { auto __action_object = action_object; + + LOG(DEBUG) << &session << "HttpAction" << "OnRequestBody"; __action_object->OnRequestBody(&session); }); session->SetResponseHeaderCallback([action_object](HttpSession & session) { auto __action_object = action_object; + + LOG(DEBUG) << &session << "HttpAction" << "OnResponseHeader"; __action_object->OnResponseHeader(&session); }); session->SetResponseBodyCallback([action_object](HttpSession & session) { auto __action_object = action_object; + + LOG(DEBUG) << &session << "HttpAction" << "OnResponseBody"; __action_object->OnResponseBody(&session); }); |
