Skip to content

Commit 8ed6340

Browse files
authored
Fix POST args with empty values not being parsed (issue #268) (#351)
* Fix POST args with empty values not being parsed (issue #268) Destroy post processor before invoking the callback to ensure pending POST body keys with empty values are properly finalized. Without this, POST data like "arg1=val1&arg2=" would fail to include arg2. The fix moves MHD_destroy_post_processor call to finalize_answer() before the resource callback is invoked, rather than waiting for the modded_request destructor. Added test cases verifying: - arg1=val1&arg2= correctly parses both args - arg1= correctly parses as empty string - arg1=&arg2= correctly parses both as empty strings This supersedes PR #269 with the requested code style changes. * Fix Windows build: replace rand_r with portable rand() rand_r is POSIX-specific and not available on Windows. Since this is just a stress test where thread-safe randomness isn't critical, use the portable rand() function instead.
1 parent 57522ba commit 8ed6340

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

src/webserver.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@ MHD_Result webserver::finalize_answer(MHD_Connection* connection, struct details
738738

739739
if (found) {
740740
try {
741+
if (mr->pp != nullptr) {
742+
MHD_destroy_post_processor(mr->pp);
743+
mr->pp = nullptr;
744+
}
741745
if (hrm->is_allowed(method)) {
742746
mr->dhrs = ((hrm)->*(mr->callback))(*mr->dhr); // copy in memory (move in case)
743747
if (mr->dhrs.get() == nullptr || mr->dhrs->get_response_code() == -1) {

test/integ/basic.cpp

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,62 @@ LT_BEGIN_AUTO_TEST(basic_suite, empty_arg)
993993
curl_easy_cleanup(curl);
994994
LT_END_AUTO_TEST(empty_arg)
995995

996+
LT_BEGIN_AUTO_TEST(basic_suite, empty_arg_value_at_end)
997+
// Test for issue #268: POST body keys without values at the end
998+
// are not processed when using application/x-www-form-urlencoded
999+
simple_resource resource;
1000+
LT_ASSERT_EQ(true, ws->register_resource("base", &resource));
1001+
curl_global_init(CURL_GLOBAL_ALL);
1002+
1003+
// Test case 1: arg2 has empty value at end (the bug case)
1004+
{
1005+
string s;
1006+
CURL *curl = curl_easy_init();
1007+
CURLcode res;
1008+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base");
1009+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "arg1=val1&arg2=");
1010+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1011+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1012+
res = curl_easy_perform(curl);
1013+
LT_ASSERT_EQ(res, 0);
1014+
// arg1="val1", arg2="" -> response should be "val1"
1015+
LT_CHECK_EQ(s, "val1");
1016+
curl_easy_cleanup(curl);
1017+
}
1018+
1019+
// Test case 2: only arg1 with empty value
1020+
{
1021+
string s;
1022+
CURL *curl = curl_easy_init();
1023+
CURLcode res;
1024+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base");
1025+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "arg1=");
1026+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1027+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1028+
res = curl_easy_perform(curl);
1029+
LT_ASSERT_EQ(res, 0);
1030+
// arg1="" -> response should be ""
1031+
LT_CHECK_EQ(s, "");
1032+
curl_easy_cleanup(curl);
1033+
}
1034+
1035+
// Test case 3: both args with empty values
1036+
{
1037+
string s;
1038+
CURL *curl = curl_easy_init();
1039+
CURLcode res;
1040+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base");
1041+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "arg1=&arg2=");
1042+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1043+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1044+
res = curl_easy_perform(curl);
1045+
LT_ASSERT_EQ(res, 0);
1046+
// arg1="", arg2="" -> response should be ""
1047+
LT_CHECK_EQ(s, "");
1048+
curl_easy_cleanup(curl);
1049+
}
1050+
LT_END_AUTO_TEST(empty_arg_value_at_end)
1051+
9961052
LT_BEGIN_AUTO_TEST(basic_suite, no_response)
9971053
no_response_resource resource;
9981054
LT_ASSERT_EQ(true, ws->register_resource("base", &resource));
@@ -1588,12 +1644,10 @@ LT_BEGIN_AUTO_TEST(basic_suite, thread_safety)
15881644
});
15891645

15901646
auto get_thread = std::thread([&](){
1591-
unsigned int seed = 42;
15921647
while (!done) {
15931648
CURL *curl = curl_easy_init();
15941649
std::string s;
1595-
std::string url = "localhost:" PORT_STRING "/route" + std::to_string(
1596-
static_cast<int>((rand_r(&seed) * 10000000.0) / RAND_MAX));
1650+
std::string url = "localhost:" PORT_STRING "/route" + std::to_string(rand() % 10000000); // NOLINT(runtime/threadsafe_fn)
15971651
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
15981652
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
15991653
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);

0 commit comments

Comments
 (0)