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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]