This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new b7092a79 Reject http request without host (#2600)
b7092a79 is described below

commit b7092a79aa6aa2c5ec6b41cf6376e17054d43806
Author: Bright Chen <chenguangmin...@foxmail.com>
AuthorDate: Mon Jun 3 16:48:38 2024 +0800

    Reject http request without host (#2600)
---
 src/brpc/details/http_message.cpp   | 38 ++++++++++++++++++++------------
 test/brpc_http_message_unittest.cpp | 43 +++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 14 deletions(-)

diff --git a/src/brpc/details/http_message.cpp 
b/src/brpc/details/http_message.cpp
index 708f9415..3160afbf 100644
--- a/src/brpc/details/http_message.cpp
+++ b/src/brpc/details/http_message.cpp
@@ -15,9 +15,6 @@
 // specific language governing permissions and limitations
 // under the License.
 
-
-#include <cstdlib>
-
 #include <string>                               // std::string
 #include <iostream>
 #include <gflags/gflags.h>
@@ -35,6 +32,8 @@ namespace brpc {
 
 DEFINE_bool(allow_chunked_length, false,
             "Allow both Transfer-Encoding and Content-Length headers are 
present.");
+DEFINE_bool(allow_http_1_1_request_without_host, true,
+            "Allow HTTP/1.1 request without host which violates the HTTP/1.1 
specification.");
 DEFINE_bool(http_verbose, false,
             "[DEBUG] Print EVERY http request/response");
 DEFINE_int32(http_verbose_max_body_length, 512,
@@ -150,37 +149,48 @@ int HttpMessage::on_headers_complete(http_parser *parser) 
{
         LOG(WARNING) << "Invalid major_version=" << parser->http_major;
         parser->http_major = 1;
     }
-    http_message->header().set_version(parser->http_major, parser->http_minor);
+    HttpHeader& headers = http_message->header();
+    headers.set_version(parser->http_major, parser->http_minor);
     // Only for response
     // http_parser may set status_code to 0 when the field is not needed,
     // e.g. in a request. In principle status_code is undefined in a request,
     // but to be consistent and not surprise users, we set it to OK as well.
-    http_message->header().set_status_code(
+    headers.set_status_code(
         !parser->status_code ? HTTP_STATUS_OK : parser->status_code);
     // Only for request
     // method is 0(which is DELETE) for response as well. Since users are
     // unlikely to check method of a response, we don't do anything.
-    http_message->header().set_method(static_cast<HttpMethod>(parser->method));
-    if (parser->type == HTTP_REQUEST &&
-        http_message->header().uri().SetHttpURL(http_message->_url) != 0) {
+    headers.set_method(static_cast<HttpMethod>(parser->method));
+    bool is_http_request = parser->type == HTTP_REQUEST;
+    if (is_http_request && headers.uri().SetHttpURL(http_message->_url) != 0) {
         LOG(ERROR) << "Fail to parse url=`" << http_message->_url << '\'';
         return -1;
     }
-    //rfc2616-sec5.2
+    // https://datatracker.ietf.org/doc/html/rfc2616#section-5.2
     //1. If Request-URI is an absoluteURI, the host is part of the Request-URI.
     //Any Host header field value in the request MUST be ignored.
     //2. If the Request-URI is not an absoluteURI, and the request includes a
     //Host header field, the host is determined by the Host header field value.
     //3. If the host as determined by rule 1 or 2 is not a valid host on the
-    //server, the responce MUST be a 400 error messsage.
-    URI & uri = http_message->header().uri();
+    //server, the responce MUST be a 400 (Bad Request) error messsage.
+    URI& uri = headers.uri();
     if (uri._host.empty()) {
-        const std::string* host_header = 
http_message->header().GetHeader("host");
+        const std::string* host_header = headers.GetHeader("host");
         if (host_header != NULL) {
             uri.SetHostAndPort(*host_header);
         }
     }
 
+    // https://datatracker.ietf.org/doc/html/rfc2616#section-14.23
+    // All Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad 
Request)
+    // status code to any HTTP/1.1 request message which lacks a Host header 
field.
+    if (uri.host().empty() && is_http_request &&
+        !headers.before_http_1_1() &&
+        !FLAGS_allow_http_1_1_request_without_host) {
+        LOG(ERROR) << "HTTP protocol error: missing host header";
+        return -1;
+    }
+
     // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
     // If a message is received with both a Transfer-Encoding and a
     // Content-Length header field, the Transfer-Encoding overrides the
@@ -195,9 +205,9 @@ int HttpMessage::on_headers_complete(http_parser *parser) {
     // is chunked - remove Content-Length and serve request.
     if (parser->uses_transfer_encoding && parser->flags & F_CONTENTLENGTH) {
         if (parser->flags & F_CHUNKED && FLAGS_allow_chunked_length) {
-            http_message->header().RemoveHeader("Content-Length");
+            headers.RemoveHeader("Content-Length");
         } else {
-            LOG(ERROR) << "HTTP/1.1 protocol error: both Content-Length "
+            LOG(ERROR) << "HTTP protocol error: both Content-Length "
                        << "and Transfer-Encoding are set.";
             return -1;
         }
diff --git a/test/brpc_http_message_unittest.cpp 
b/test/brpc_http_message_unittest.cpp
index 651397aa..e9f25e8f 100644
--- a/test/brpc_http_message_unittest.cpp
+++ b/test/brpc_http_message_unittest.cpp
@@ -28,6 +28,14 @@
 namespace brpc {
 
 DECLARE_bool(allow_chunked_length);
+DECLARE_bool(allow_http_1_1_request_without_host);
+
+int main(int argc, char* argv[]) {
+    testing::InitGoogleTest(&argc, argv);
+    GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true);
+    brpc::FLAGS_allow_http_1_1_request_without_host = true;
+    return RUN_ALL_TESTS();
+}
 
 namespace policy {
 Server::MethodProperty*
@@ -692,4 +700,39 @@ TEST(HttpMessageTest, serialize_http_response) {
         << butil::ToPrintable(response);
 }
 
+TEST(HttpMessageTest, http_1_1_request_without_host) {
+    brpc::FLAGS_allow_http_1_1_request_without_host = false;
+    {
+        butil::IOBuf request;
+        request.append("GET /service/method HTTP/1.1\r\n"
+                       "Content-Type: text/plain\r\n\r\n");
+
+        brpc::HttpMessage http_message;
+        ASSERT_TRUE(http_message.ParseFromIOBuf(request) < 0);
+    }
+    {
+        butil::IOBuf request;
+        request.append("GET http://baidu.com/service/method HTTP/1.1\r\n"
+                       "Content-Type: text/plain\r\n\r\n");
+
+        brpc::HttpMessage http_message;
+        ASSERT_TRUE(http_message.ParseFromIOBuf(request) >= 0);
+        ASSERT_TRUE(http_message.Completed());
+        ASSERT_EQ("text/plain", http_message.header().content_type());
+    }
+    {
+        butil::IOBuf request;
+        request.append("GET /service/method HTTP/1.1\r\n"
+                       "Content-Type: text/plain\r\n"
+                       "Host: baidu.com\r\n\r\n");
+
+        brpc::HttpMessage http_message;
+        ASSERT_GE(http_message.ParseFromIOBuf(request), 0);
+        ASSERT_GE(http_message.ParseFromArray(NULL, 0), 0);
+        ASSERT_TRUE(http_message.Completed());
+        ASSERT_EQ("text/plain", http_message.header().content_type());
+    }
+    brpc::FLAGS_allow_http_1_1_request_without_host = true;
+}
+
 } //namespace


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@brpc.apache.org
For additional commands, e-mail: dev-h...@brpc.apache.org

Reply via email to