@@ -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+
64006474net::awaitable<void > proxy_session::unauthorized_http_route (const string_request& request) noexcept
64016475{
64026476 boost::system::error_code ec;
0 commit comments