Skip to content

Commit eaab802

Browse files
committed
add 405 method check and HEAD response for static web server
1 parent b7e37e7 commit eaab802

2 files changed

Lines changed: 91 additions & 14 deletions

File tree

‎proxy/include/proxy/proxy_session.hpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,9 @@ namespace proxy {
909909
// 返回 HTTP 403 Forbidden 响应.
910910
net::awaitable<void> forbidden_http_route(const string_request& request) noexcept;
911911

912+
// 返回 HTTP 405 Method Not Allowed 响应 (含 Allow 头).
913+
net::awaitable<void> method_not_allowed_http_route(const string_request& request) noexcept;
914+
912915
// 返回 HTTP 401 Unauthorized 响应.
913916
net::awaitable<void> unauthorized_http_route(const string_request& request) noexcept;
914917

‎proxy/src/proxy_session.cpp‎

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ R"x*x*x(<html>
8080
<hr><center>nginx/1.20.2</center>
8181
</body>
8282
</html>
83+
)x*x*x";
84+
85+
static const char* fake_405_content =
86+
R"x*x*x(<html>
87+
<head><title>405 Method Not Allowed</title></head>
88+
<body>
89+
<center><h1>405 Method Not Allowed</h1></center>
90+
<hr><center>nginx/1.20.2</center>
91+
</body>
92+
</html>
8393
)x*x*x";
8494

8595
static const char* fake_404_content_fmt =
@@ -665,10 +675,17 @@ R"x*x*x(<html>
665675
res.prepare_payload();
666676

667677
http::serializer<false, string_body, http::fields> sr(res);
668-
co_await http::async_write(
669-
m_local_socket,
670-
sr,
671-
net_awaitable[ec]);
678+
// HEAD 请求只返回响应头, 不发送 body.
679+
if (request.method() == http::verb::head)
680+
co_await http::async_write_header(
681+
m_local_socket,
682+
sr,
683+
net_awaitable[ec]);
684+
else
685+
co_await http::async_write(
686+
m_local_socket,
687+
sr,
688+
net_awaitable[ec]);
672689
if (ec)
673690
{
674691
log_conn_warning()
@@ -4834,6 +4851,21 @@ R"x*x*x(<html>
48344851
co_return;
48354852
}
48364853

4854+
// 静态文件服务只支持 GET/HEAD 方法, 其他方法返回 405.
4855+
// /dns-query 为 DNS over HTTPS 接口, 单独支持 POST (由
4856+
// on_http_dns_query 处理), 因此需要排除该路由.
4857+
bool dns_proxy = m_option.dns_upstream_.has_value();
4858+
if (req.method() != http::verb::get &&
4859+
req.method() != http::verb::head &&
4860+
!(dns_proxy &&
4861+
boost::regex_match(
4862+
std::string(req.target()),
4863+
boost::regex{R"(^\/dns-query(.*)$)"})))
4864+
{
4865+
co_await method_not_allowed_http_route(req);
4866+
co_return;
4867+
}
4868+
48374869
std::string target = req.target();
48384870
boost::smatch what;
48394871
http_context http_ctx{
@@ -4856,8 +4888,6 @@ R"x*x*x(<html>
48564888
fake_400_content, \
48574889
http::status::bad_request ); }
48584890

4859-
bool dns_proxy = m_option.dns_upstream_.has_value();
4860-
48614891
BEGIN_HTTP_ROUTE()
48624892
ON_HTTP_ROUTE(true, R"(^(.*)?\/$)", on_http_dir)
48634893
ON_HTTP_ROUTE(true, R"(^(.*)?(\/\?r=json.*)$)", on_http_all_json)
@@ -5019,10 +5049,17 @@ R"x*x*x(<html>
50195049
res.prepare_payload();
50205050

50215051
http::serializer<false, string_body, http::fields> sr(res);
5022-
co_await http::async_write(
5023-
m_local_socket,
5024-
sr,
5025-
net_awaitable[ec]);
5052+
// HEAD 请求只返回响应头, 不发送 body.
5053+
if (request.method() == http::verb::head)
5054+
co_await http::async_write_header(
5055+
m_local_socket,
5056+
sr,
5057+
net_awaitable[ec]);
5058+
else
5059+
co_await http::async_write(
5060+
m_local_socket,
5061+
sr,
5062+
net_awaitable[ec]);
50265063
if (ec)
50275064
{
50285065
log_conn_warning()
@@ -5083,10 +5120,17 @@ R"x*x*x(<html>
50835120
res.prepare_payload();
50845121

50855122
http::serializer<false, string_body, http::fields> sr(res);
5086-
co_await http::async_write(
5087-
m_local_socket,
5088-
sr,
5089-
net_awaitable[ec]);
5123+
// HEAD 请求只返回响应头, 不发送 body.
5124+
if (request.method() == http::verb::head)
5125+
co_await http::async_write_header(
5126+
m_local_socket,
5127+
sr,
5128+
net_awaitable[ec]);
5129+
else
5130+
co_await http::async_write(
5131+
m_local_socket,
5132+
sr,
5133+
net_awaitable[ec]);
50905134
if (ec)
50915135
{
50925136
log_conn_warning()
@@ -5295,6 +5339,10 @@ R"x*x*x(<html>
52955339
co_return;
52965340
}
52975341

5342+
// HEAD 请求只返回响应头, 不发送文件内容.
5343+
if (request.method() == http::verb::head)
5344+
co_return;
5345+
52985346
auto buf_size = 5 * 1024 * 1024;
52995347
if (m_option.tcp_rate_limit_ > 0 && m_option.tcp_rate_limit_ < buf_size)
53005348
buf_size = m_option.tcp_rate_limit_;
@@ -6397,6 +6445,32 @@ R"x*x*x(<html>
63976445
co_return;
63986446
}
63996447

6448+
net::awaitable<void> proxy_session::method_not_allowed_http_route(const string_request& request) noexcept
6449+
{
6450+
boost::system::error_code ec;
6451+
6452+
// 405 响应必须携带 Allow 头, 声明服务端支持的方法.
6453+
string_response res{ http::status::method_not_allowed, request.version() };
6454+
res.set(http::field::server, version_string);
6455+
res.set(http::field::date, server_date_string());
6456+
res.set(http::field::content_type, "text/html");
6457+
res.set(http::field::allow, "GET, HEAD");
6458+
res.keep_alive(true);
6459+
res.body() = fake_405_content;
6460+
res.prepare_payload();
6461+
6462+
http::serializer<false, string_body, http::fields> sr(res);
6463+
co_await http::async_write(m_local_socket, sr, net_awaitable[ec]);
6464+
if (ec)
6465+
{
6466+
log_conn_warning()
6467+
<< ", method not allowed http route err: "
6468+
<< ec.message();
6469+
}
6470+
6471+
co_return;
6472+
}
6473+
64006474
net::awaitable<void> proxy_session::unauthorized_http_route(const string_request& request) noexcept
64016475
{
64026476
boost::system::error_code ec;

0 commit comments

Comments
 (0)