summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLu <[email protected]>2018-07-18 20:07:55 +0800
committerLu <[email protected]>2018-07-18 20:07:55 +0800
commit961158e968761cc615ec058e77ece1711c9cbd01 (patch)
treea9f6346c6f3f12e83c42d333aa62f6cf42b2bedd
parent992fdd27ed695f9285eff808e4bc184aeee52b89 (diff)
#2 基本实现HTTP应答侧内容替换功能,支持对HTTP应答头部和应答体的内容替换。
-rw-r--r--src/http.h10
-rw-r--r--src/http1.cc95
-rw-r--r--src/httpaction.cc103
-rw-r--r--src/httpscan.cc11
4 files changed, 181 insertions, 38 deletions
diff --git a/src/http.h b/src/http.h
index 21bf4ac..54e78e8 100644
--- a/src/http.h
+++ b/src/http.h
@@ -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);
});