diff --git a/src/http_request.cpp b/src/http_request.cpp index b0cd46b3..4d52eb43 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -105,20 +105,31 @@ const http::header_view_map http_request::get_cookies() const { } std::string_view http_request::get_arg(std::string_view key) const { - std::map::const_iterator it = cache->unescaped_args.find(std::string(key)); + auto const it = cache->unescaped_args.find(std::string(key)); if (it != cache->unescaped_args.end()) { - return it->second; + return it->second.front(); } return get_connection_value(key, MHD_GET_ARGUMENT_KIND); } +static void fill_arg_view_map(http::arg_view_map *arguments, const http::arg_map& cached_map) { + for (const auto& kv : cached_map) { + std::vector vec; + vec.reserve(kv.second.size()); + for (const auto& value : kv.second) { + vec.push_back(value); + } + arguments->emplace(std::string_view(kv.first), std::move(vec)); + } +} + const http::arg_view_map http_request::get_args() const { http::arg_view_map arguments; if (!cache->unescaped_args.empty()) { - arguments.insert(cache->unescaped_args.begin(), cache->unescaped_args.end()); + fill_arg_view_map(&arguments, cache->unescaped_args); return arguments; } @@ -128,7 +139,7 @@ const http::arg_view_map http_request::get_args() const { MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, reinterpret_cast(&aa)); - arguments.insert(cache->unescaped_args.begin(), cache->unescaped_args.end()); + fill_arg_view_map(&arguments, cache->unescaped_args); return arguments; } @@ -155,7 +166,13 @@ MHD_Result http_request::build_request_args(void *cls, enum MHD_ValueKind kind, std::string value = ((arg_value == nullptr) ? "" : arg_value); http::base_unescaper(&value, aa->unescaper); - (*aa->arguments)[key] = value; + auto existing_iter = aa->arguments->find(key); + if (existing_iter != aa->arguments->end()) { + // Key exists, add value to collection instead of overwriting previous value. + existing_iter->second.push_back(value); + } else { + (*aa->arguments)[key] = {value}; + } return MHD_YES; } diff --git a/src/http_utils.cpp b/src/http_utils.cpp index d34e196b..bfae1753 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -525,11 +525,35 @@ void dump_map(std::ostream& os, const std::string& prefix, const map_t& map) { } void dump_header_map(std::ostream& os, const std::string& prefix, const http::header_view_map &map) { - dump_map(os, prefix, map); + auto it = map.begin(); + auto end = map.end(); + + if (map.size()) { + os << " " << prefix << " ["; + for (; it != end; ++it) { + os << (*it).first << ":\"" << (*it).second << "\" "; + } + os << "]" << std::endl; + } } void dump_arg_map(std::ostream& os, const std::string& prefix, const http::arg_view_map &map) { - dump_map(os, prefix, map); + auto it = map.begin(); + auto end = map.end(); + + if (map.size()) { + os << " " << prefix << " ["; + for (; it != end; ++it) { + os << (*it).first << ":["; + std::string delim = ""; + for (const auto & v : it->second) { + os << delim << "\"" << v << "\""; + delim = ", "; + } + os << "] "; + } + os << "]" << std::endl; + } } size_t base_unescaper(std::string* s, unescaper_ptr unescaper) { diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 6228c82f..577b90c5 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -173,8 +173,8 @@ class http_request { /** * Method used to get a specific argument passed with the request. - * @param ket the specific argument to get the value from - * @return the value of the arg. + * @param key the specific argument to get the value from + * @return the value of the first arg matching the key. **/ std::string_view get_arg(std::string_view key) const; @@ -298,7 +298,12 @@ class http_request { * @param value The value assumed by the argument **/ void set_arg(const std::string& key, const std::string& value) { - cache->unescaped_args[key] = value.substr(0, content_size_limit); + auto existing_iter = cache->unescaped_args.find(key); + if (existing_iter != cache->unescaped_args.end()) { + existing_iter->second.push_back(value.substr(0, content_size_limit)); + } else { + cache->unescaped_args[key] = {value.substr(0, content_size_limit)}; + } } /** @@ -308,7 +313,13 @@ class http_request { * @param size The size in number of char of the value parameter. **/ void set_arg(const char* key, const char* value, size_t size) { - cache->unescaped_args[key] = std::string(value, std::min(size, content_size_limit)); + auto value_str = std::string(value, std::min(size, content_size_limit)); + auto existing_iter = cache->unescaped_args.find(key); + if (existing_iter != cache->unescaped_args.end()) { + existing_iter->second.push_back(value_str); + } else { + cache->unescaped_args[key] = {value_str}; + } } /** @@ -365,10 +376,13 @@ class http_request { * Method used to set all arguments of the request. * @param args The args key-value map to set for the request. **/ - void set_args(const std::map& args) { - std::map::const_iterator it; - for (it = args.begin(); it != args.end(); ++it) { - this->cache->unescaped_args[it->first] = it->second.substr(0, content_size_limit); + void set_args(const std::map>& args) { + for (auto it = args.begin(); it != args.end(); ++it) { + auto value_vec = it->second; + std::transform(value_vec.begin(), value_vec.end(), value_vec.begin(), [this](const std::string& s) { + return s.substr(0, content_size_limit); + }); + this->cache->unescaped_args[it->first] = std::move(value_vec); } } diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index e6b5411f..7ee76e43 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -318,8 +318,8 @@ class arg_comparator { using header_map = std::map; using header_view_map = std::map; -using arg_map = std::map; -using arg_view_map = std::map; +using arg_map = std::map, http::arg_comparator>; +using arg_view_map = std::map, http::arg_comparator>; struct ip_representation { http_utils::IP_version_T ip_version; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index c6e61d85..d390db4b 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -116,7 +116,10 @@ class header_reading_resource : public http_resource { class full_args_resource : public http_resource { public: shared_ptr render_GET(const http_request& req) { - return shared_ptr(new string_response(std::string(req.get_args().at("arg")), 200, "text/plain")); + const auto args = req.get_args(); + const auto arg = args.at("arg"); + const auto resp = std::string(arg[0]) + std::string(arg[1]); + return shared_ptr(new string_response(resp, 200, "text/plain")); } }; @@ -772,13 +775,13 @@ LT_BEGIN_AUTO_TEST(basic_suite, full_arguments_processing) string s; CURL *curl = curl_easy_init(); CURLcode res; - curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=argument"); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=argument&arg=another"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); - LT_CHECK_EQ(s, "argument"); + LT_CHECK_EQ(s, "argumentanother"); curl_easy_cleanup(curl); LT_END_AUTO_TEST(full_arguments_processing) diff --git a/test/integ/file_upload.cpp b/test/integ/file_upload.cpp index 2082f9c7..303c6672 100644 --- a/test/integ/file_upload.cpp +++ b/test/integ/file_upload.cpp @@ -144,7 +144,7 @@ class print_file_upload_resource : public http_resource { auto args_view = req.get_args(); // req may go out of scope, so we need to copy the values. for (auto const& item : args_view) { - args[std::string(item.first)] = std::string(item.second); + args[std::string(item.first)] = {std::string(item.second.front())}; } files = req.get_files(); shared_ptr hresp(new string_response("OK", 201, "text/plain")); @@ -156,7 +156,7 @@ class print_file_upload_resource : public http_resource { auto args_view = req.get_args(); // req may go out of scope, so we need to copy the values. for (auto const& item : args_view) { - args[std::string(item.first)] = std::string(item.second); + args[std::string(item.first)] = {std::string(item.second.front())}; } files = req.get_files(); shared_ptr hresp(new string_response("OK", 200, "text/plain")); @@ -218,7 +218,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk) LT_CHECK_EQ(args.size(), 1); auto arg = args.begin(); LT_CHECK_EQ(arg->first, TEST_KEY); - LT_CHECK_EQ(arg->second, TEST_CONTENT); + LT_CHECK_EQ(arg->second.front(), TEST_CONTENT); map> files = resource.get_files(); LT_CHECK_EQ(files.size(), 1); @@ -300,10 +300,10 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_additional_par LT_CHECK_EQ(args.size(), 2); auto arg = args.begin(); LT_CHECK_EQ(arg->first, TEST_KEY); - LT_CHECK_EQ(arg->second, TEST_CONTENT); + LT_CHECK_EQ(arg->second.front(), TEST_CONTENT); arg++; LT_CHECK_EQ(arg->first, TEST_PARAM_KEY); - LT_CHECK_EQ(arg->second, TEST_PARAM_VALUE); + LT_CHECK_EQ(arg->second.front(), TEST_PARAM_VALUE); map> files = resource.get_files(); LT_CHECK_EQ(files.size(), 1); @@ -354,10 +354,10 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_two_files) LT_CHECK_EQ(args.size(), 2); auto arg = args.begin(); LT_CHECK_EQ(arg->first, TEST_KEY); - LT_CHECK_EQ(arg->second, TEST_CONTENT); + LT_CHECK_EQ(arg->second.front(), TEST_CONTENT); arg++; LT_CHECK_EQ(arg->first, TEST_KEY_2); - LT_CHECK_EQ(arg->second, TEST_CONTENT_2); + LT_CHECK_EQ(arg->second.front(), TEST_CONTENT_2); map> files = resource.get_files(); LT_CHECK_EQ(files.size(), 2); @@ -461,7 +461,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_only_incl_content) LT_CHECK_EQ(args.size(), 1); auto arg = args.begin(); LT_CHECK_EQ(arg->first, TEST_KEY); - LT_CHECK_EQ(arg->second, TEST_CONTENT); + LT_CHECK_EQ(arg->second.front(), TEST_CONTENT); map> files = resource.get_files(); LT_CHECK_EQ(resource.get_files().size(), 0); @@ -493,7 +493,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_only_excl_content) LT_CHECK_EQ(args.size(), 1); auto arg = args.begin(); LT_CHECK_EQ(arg->first, TEST_KEY); - LT_CHECK_EQ(arg->second, TEST_CONTENT); + LT_CHECK_EQ(arg->second.front(), TEST_CONTENT); map> files = resource.get_files(); LT_CHECK_EQ(files.size(), 0); diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 1b7486ac..4fe8e361 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -618,25 +618,25 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, dump_header_map_no_prefix) LT_END_AUTO_TEST(dump_header_map_no_prefix) LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map) - std::map arg_map; - arg_map["ARG_ONE"] = "VALUE_ONE"; - arg_map["ARG_TWO"] = "VALUE_TWO"; - arg_map["ARG_THREE"] = "VALUE_THREE"; + std::map, httpserver::http::arg_comparator> arg_map; + arg_map["ARG_ONE"] = {"VALUE_ONE", "VALUE_ONE_2"}; + arg_map["ARG_TWO"] = {"VALUE_TWO"}; + arg_map["ARG_THREE"] = {"VALUE_THREE"}; std::stringstream ss; httpserver::http::dump_arg_map(ss, "prefix", arg_map); - LT_CHECK_EQ(ss.str(), " prefix [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); + LT_CHECK_EQ(ss.str(), " prefix [ARG_ONE:[\"VALUE_ONE\", \"VALUE_ONE_2\"] ARG_TWO:[\"VALUE_TWO\"] ARG_THREE:[\"VALUE_THREE\"] ]\n"); LT_END_AUTO_TEST(dump_arg_map) LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map_no_prefix) - std::map arg_map; - arg_map["ARG_ONE"] = "VALUE_ONE"; - arg_map["ARG_TWO"] = "VALUE_TWO"; - arg_map["ARG_THREE"] = "VALUE_THREE"; + std::map, httpserver::http::arg_comparator> arg_map; + arg_map["ARG_ONE"] = {"VALUE_ONE", "VALUE_ONE_2"}; + arg_map["ARG_TWO"] = {"VALUE_TWO"}; + arg_map["ARG_THREE"] = {"VALUE_THREE"}; std::stringstream ss; httpserver::http::dump_arg_map(ss, "", arg_map); - LT_CHECK_EQ(ss.str(), " [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); + LT_CHECK_EQ(ss.str(), " [ARG_ONE:[\"VALUE_ONE\", \"VALUE_ONE_2\"] ARG_TWO:[\"VALUE_TWO\"] ARG_THREE:[\"VALUE_THREE\"] ]\n"); LT_END_AUTO_TEST(dump_arg_map_no_prefix) LT_BEGIN_AUTO_TEST_ENV()