diff --git a/httplib.h b/httplib.h index 65c644e..fb45a95 100644 --- a/httplib.h +++ b/httplib.h @@ -3794,6 +3794,7 @@ public: switch (state_) { case 0: { // Initial boundary auto pattern = dash_ + boundary_ + crlf_; + buf_erase(buf_find(pattern)); if (pattern.size() > buf_size()) { return true; } if (!buf_start_with(pattern)) { return false; } buf_erase(pattern.size()); @@ -3887,17 +3888,13 @@ public: if (buf_start_with(pattern)) { buf_erase(pattern.size()); is_valid_ = true; - state_ = 5; + buf_erase(buf_size()); // Remove epilogue } else { return true; } } break; } - case 5: { // Done - is_valid_ = false; - return false; - } } } diff --git a/test/test.cc b/test/test.cc index 322b208..3f94e58 100644 --- a/test/test.cc +++ b/test/test.cc @@ -3015,8 +3015,10 @@ TEST(GzipDecompressor, ChunkedDecompression) { httplib::detail::gzip_compressor compressor; bool result = compressor.compress( data.data(), data.size(), - /*last=*/true, [&](const char *compressed_data_chunk, size_t compressed_data_size) { - compressed_data.insert(compressed_data.size(), compressed_data_chunk, compressed_data_size); + /*last=*/true, + [&](const char *compressed_data_chunk, size_t compressed_data_size) { + compressed_data.insert(compressed_data.size(), compressed_data_chunk, + compressed_data_size); return true; }); ASSERT_TRUE(result); @@ -3035,8 +3037,11 @@ TEST(GzipDecompressor, ChunkedDecompression) { std::min(compressed_data.size() - chunk_begin, chunk_size); bool result = decompressor.decompress( compressed_data.data() + chunk_begin, current_chunk_size, - [&](const char *decompressed_data_chunk, size_t decompressed_data_chunk_size) { - decompressed_data.insert(decompressed_data.size(), decompressed_data_chunk, decompressed_data_chunk_size); + [&](const char *decompressed_data_chunk, + size_t decompressed_data_chunk_size) { + decompressed_data.insert(decompressed_data.size(), + decompressed_data_chunk, + decompressed_data_chunk_size); return true; }); ASSERT_TRUE(result); @@ -4974,5 +4979,48 @@ TEST(MultipartFormDataTest, LargeData) { svr.stop(); t.join(); } + +TEST(MultipartFormDataTest, WithPreamble) { + Server svr; + svr.Post("/post", [&](const Request &req, Response &res) { + res.set_content("ok", "text/plain"); + }); + + thread t = thread([&] { svr.listen(HOST, PORT); }); + while (!svr.is_running()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + const std::string body = + "This is the preamble. It is to be ignored, though it\r\n" + "is a handy place for composition agents to include an\r\n" + "explanatory note to non-MIME conformant readers.\r\n" + "\r\n" + "\r\n" + "--simple boundary\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "value1\r\n" + "--simple boundary\r\n" + "Content-Disposition: form-data; name=\"field2\"; " + "filename=\"example.txt\"\r\n" + "\r\n" + "value2\r\n" + "--simple boundary--\r\n" + "This is the epilogue. It is also to be ignored.\r\n"; + + std::string content_type = + R"(multipart/form-data; boundary="simple boundary")"; + + Client cli(HOST, PORT); + auto res = cli.Post("/post", body, content_type.c_str()); + + ASSERT_TRUE(res); + EXPECT_EQ(200, res->status); + + svr.stop(); + t.join(); +} + #endif