From df74526f910e6ad23d4d4ce608b5eb747d230b62 Mon Sep 17 00:00:00 2001 From: Mathieu Gaillard Date: Wed, 8 Mar 2023 20:57:17 -0800 Subject: [PATCH] Fix multipart Content-Type headers with both boundary and charset parameters (#1516) * Fix multipart Content-Type headers with both boundary and charset parameters * Improve code readability * Add missing forward declaration --------- Co-authored-by: Mathieu Gaillard --- httplib.h | 9 +++++++-- test/test.cc | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/httplib.h b/httplib.h index cc39270..67e5516 100644 --- a/httplib.h +++ b/httplib.h @@ -1823,6 +1823,8 @@ std::string params_to_query_str(const Params ¶ms); void parse_query_text(const std::string &s, Params ¶ms); +bool parse_multipart_boundary(const std::string &content_type, std::string &boundary); + bool parse_range_header(const std::string &s, Ranges &ranges); int close_socket(socket_t sock); @@ -3888,9 +3890,12 @@ inline void parse_query_text(const std::string &s, Params ¶ms) { inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) { - auto pos = content_type.find("boundary="); + auto boundary_keyword = "boundary="; + auto pos = content_type.find(boundary_keyword); if (pos == std::string::npos) { return false; } - boundary = content_type.substr(pos + 9); + auto end = content_type.find(';', pos); + auto beg = pos + strlen(boundary_keyword); + boundary = content_type.substr(beg, end - beg); if (boundary.length() >= 2 && boundary.front() == '"' && boundary.back() == '"') { boundary = boundary.substr(1, boundary.size() - 2); diff --git a/test/test.cc b/test/test.cc index bbb9d0d..173a1a4 100644 --- a/test/test.cc +++ b/test/test.cc @@ -169,6 +169,39 @@ TEST(ParamsToQueryTest, ConvertParamsToQuery) { EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1&key2=val2&key3=val3"); } +TEST(ParseMultipartBoundaryTest, DefaultValue) { + string content_type = "multipart/form-data; boundary=something"; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "something"); +} + +TEST(ParseMultipartBoundaryTest, ValueWithQuote) { + string content_type = "multipart/form-data; boundary=\"gc0pJq0M:08jU534c0p\""; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "gc0pJq0M:08jU534c0p"); +} + +TEST(ParseMultipartBoundaryTest, ValueWithCharset) { + string content_type = "multipart/mixed; boundary=THIS_STRING_SEPARATES;charset=UTF-8"; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "THIS_STRING_SEPARATES"); +} + +TEST(ParseMultipartBoundaryTest, ValueWithQuotesAndCharset) { + string content_type = + "multipart/mixed; boundary=\"cpp-httplib-multipart-data\"; charset=UTF-8"; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "cpp-httplib-multipart-data"); +} + TEST(GetHeaderValueTest, DefaultValue) { Headers headers = {{"Dummy", "Dummy"}}; auto val = detail::get_header_value(headers, "Content-Type", 0, "text/plain");