Skip to content

Commit

Permalink
set max http body length
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos committed Dec 10, 2024
1 parent d501a21 commit d036aa9
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 19 deletions.
15 changes: 14 additions & 1 deletion include/cinatra/coro_http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,10 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {

void set_ws_sec_key(std::string sec_key) { ws_sec_key_ = std::move(sec_key); }

void set_max_http_body_size(int64_t max_size) {
max_http_body_len_ = max_size;
}

async_simple::coro::Lazy<resp_data> read_websocket() {
co_return co_await async_read_ws();
}
Expand Down Expand Up @@ -1629,9 +1633,17 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
parse_ret = -1;
}
#endif
if (parse_ret < 0) {
if (parse_ret < 0) [[unlikely]] {
return std::make_error_code(std::errc::protocol_error);
}

if (parser_.body_len() > max_http_body_len_ || parser_.body_len() < 0)
[[unlikely]] {
CINATRA_LOG_ERROR << "invalid http content length: "
<< parser_.body_len();
return std::make_error_code(std::errc::invalid_argument);
}

head_buf_.consume(header_size); // header size
data.resp_headers = parser.get_headers();
data.status = parser.status();
Expand Down Expand Up @@ -2408,6 +2420,7 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
std::string inflate_str_;
#endif
content_encoding encoding_type_ = content_encoding::none;
int64_t max_http_body_len_ = MAX_HTTP_BODY_SIZE;

#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP)
std::string uncompressed_str_;
Expand Down
18 changes: 17 additions & 1 deletion include/cinatra/coro_http_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,24 @@ class coro_http_connection
break;
}

if (parser_.body_len() > max_http_body_len_ || parser_.body_len() < 0)
[[unlikely]] {
CINATRA_LOG_ERROR << "invalid http content length: "
<< parser_.body_len();
response_.set_status_and_content(status_type::bad_request,
"invalid http content length");
co_await reply();
close();
break;
}

head_buf_.consume(size);
keep_alive_ = check_keep_alive();

auto type = request_.get_content_type();

if (type != content_type::chunked && type != content_type::multipart) {
size_t body_len = parser_.body_len();
size_t body_len = (size_t)parser_.body_len();
if (body_len == 0) {
if (parser_.method() == "GET"sv) {
if (request_.is_upgrade()) {
Expand Down Expand Up @@ -443,6 +454,10 @@ class coro_http_connection
default_handler_ = handler;
}

void set_max_http_body_size(int64_t max_size) {
max_http_body_len_ = max_size;
}

#ifdef INJECT_FOR_HTTP_SEVER_TEST
void set_write_failed_forever(bool r) { write_failed_forever_ = r; }

Expand Down Expand Up @@ -975,6 +990,7 @@ class coro_http_connection
default_handler_ = nullptr;
std::string chunk_size_str_;
std::string remote_addr_;
int64_t max_http_body_len_ = 0;
#ifdef INJECT_FOR_HTTP_SEVER_TEST
bool write_failed_forever_ = false;
bool read_failed_forever_ = false;
Expand Down
6 changes: 6 additions & 0 deletions include/cinatra/coro_http_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ class coro_http_server {

void set_no_delay(bool r) { no_delay_ = r; }

void set_max_http_body_size(int64_t max_size) {
max_http_body_len_ = max_size;
}

#ifdef CINATRA_ENABLE_SSL
void init_ssl(const std::string &cert_file, const std::string &key_file,
const std::string &passwd) {
Expand Down Expand Up @@ -680,6 +684,7 @@ class coro_http_server {
if (no_delay_) {
conn->tcp_socket().set_option(asio::ip::tcp::no_delay(true));
}
conn->set_max_http_body_size(max_http_body_len_);
if (need_shrink_every_time_) {
conn->set_shrink_to_fit(true);
}
Expand Down Expand Up @@ -1001,6 +1006,7 @@ class coro_http_server {
std::function<async_simple::coro::Lazy<void>(coro_http_request &,
coro_http_response &)>
default_handler_ = nullptr;
int64_t max_http_body_len_ = MAX_HTTP_BODY_SIZE;
#ifdef INJECT_FOR_HTTP_SEVER_TEST
bool write_failed_forever_ = false;
bool read_failed_forever_ = false;
Expand Down
2 changes: 2 additions & 0 deletions include/cinatra/define.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ inline std::unordered_map<std::string, std::string> g_content_type_map = {
struct NonSSL {};
struct SSL {};

inline constexpr int64_t MAX_HTTP_BODY_SIZE = 4294967296; // 4GB

enum class time_format {
http_format,
utc_format,
Expand Down
37 changes: 20 additions & 17 deletions include/cinatra/http_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ inline bool iequal0(std::string_view a, std::string_view b) {

class http_parser {
public:
void parse_body_len() {
auto header_value = this->get_header_value("content-length"sv);
if (header_value.empty()) {
body_len_ = 0;
}
else {
auto [ptr, ec] = std::from_chars(
header_value.data(), header_value.data() + header_value.size(),
body_len_, 10);
if (ec != std::errc{}) {
body_len_ = -1;
}
}
}

int parse_response(const char *data, size_t size, int last_len) {
int minor_version;

Expand All @@ -38,13 +53,7 @@ class http_parser {
data, size, &minor_version, &status_, &msg, &msg_len, headers_.data(),
&num_headers_, last_len);
msg_ = {msg, msg_len};
auto header_value = this->get_header_value("content-length"sv);
if (header_value.empty()) {
body_len_ = 0;
}
else {
body_len_ = atoi(header_value.data());
}
parse_body_len();
if (header_len_ < 0) [[unlikely]] {
CINATRA_LOG_WARNING << "parse http head failed";
if (num_headers_ == CINATRA_MAX_HTTP_HEADER_FIELD_SIZE) {
Expand Down Expand Up @@ -86,13 +95,7 @@ class http_parser {
body_len_ = 0;
}
else {
auto content_len = this->get_header_value("content-length"sv);
if (content_len.empty()) {
body_len_ = 0;
}
else {
body_len_ = atoi(content_len.data());
}
parse_body_len();
}

full_url_ = url_;
Expand Down Expand Up @@ -193,9 +196,9 @@ class http_parser {

int header_len() const { return header_len_; }

int body_len() const { return body_len_; }
int64_t body_len() const { return body_len_; }

int total_len() const { return header_len_ + body_len_; }
int64_t total_len() const { return header_len_ + body_len_; }

bool is_location() {
auto location = this->get_header_value("Location"sv);
Expand Down Expand Up @@ -250,7 +253,7 @@ class http_parser {
std::string_view msg_;
size_t num_headers_ = 0;
int header_len_ = 0;
int body_len_ = 0;
int64_t body_len_ = 0;
bool has_connection_{};
bool has_close_{};
bool has_upgrade_{};
Expand Down
33 changes: 33 additions & 0 deletions tests/test_cinatra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,39 @@ TEST_CASE("test ssl client") {
}
#endif

TEST_CASE("test invalid http body size") {
coro_http_server server(1, 9001);
server.set_max_http_body_size(10);
server.set_http_handler<GET, POST>(
"/get", [](coro_http_request &req, coro_http_response &resp) {
resp.set_status_and_content(status_type::ok,
"ok, it is a long test string!");
});

server.async_start();

std::string uri = "http://127.0.0.1:9001/get";
{
coro_http_client client{};
auto result =
client.post(uri, "it is a long test string!", req_content_type::text);
CHECK(result.status != 200);
}

{
coro_http_client client{};
client.set_max_http_body_size(10);
auto result = client.post(uri, "test", req_content_type::text);
CHECK(result.status != 200);
CHECK(result.net_err == std::errc::invalid_argument);
}
{
coro_http_client client{};
auto result = client.post(uri, "test", req_content_type::text);
CHECK(result.status == 200);
}
}

bool create_file(std::string_view filename, size_t file_size = 1024) {
std::ofstream out(filename.data(), std::ios::binary);
if (!out.is_open()) {
Expand Down

0 comments on commit d036aa9

Please sign in to comment.