Skip to content

Commit 914ac9e

Browse files
authored
fix #292 handle argument keys with multiple values (#302)
* fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes. * fix #292 handle argument keys with multiple values This change allows the server to handle argument keys with multiple values e.g. url?arg=param&arg=what Previous, the arg key would just be overwritten with the next value. This change adds a http_arg_value type to house all the values for a given key. Adjust existing get_arg(s) methods, and add some get_arg_flat type methods that still just return the first value for a given key. Add a test case for multiple valued keys. Add a test case for a large file upload that triggers chunking behavior of MHD. This causes the server to concatenate file chunks within the first value of the given arg key, so the test ensures that this behavior is not affected by the changes.
1 parent 17dd399 commit 914ac9e

File tree

13 files changed

+664
-61
lines changed

13 files changed

+664
-61
lines changed

configure.ac

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ AC_ARG_ENABLE([same-directory-build],
5757
AC_MSG_RESULT([$samedirectory])
5858

5959
if test x"$samedirectory" = x"no"; then
60-
if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then
60+
if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then
6161
AC_MSG_ERROR("you must configure in a separate build directory")
6262
fi
6363
fi
@@ -275,6 +275,7 @@ AC_SUBST(EXT_LIBS)
275275
AC_CONFIG_FILES([test/test_content:test/test_content])
276276
AC_CONFIG_FILES([test/test_content_2:test/test_content_2])
277277
AC_CONFIG_FILES([test/test_content_empty:test/test_content_empty])
278+
AC_CONFIG_FILES([test/test_content_large:test/test_content_large])
278279
AC_CONFIG_FILES([test/cert.pem:test/cert.pem])
279280
AC_CONFIG_FILES([test/key.pem:test/key.pem])
280281
AC_CONFIG_FILES([test/test_root_ca.pem:test/test_root_ca.pem])
@@ -292,7 +293,7 @@ AC_OUTPUT(
292293
examples/Makefile
293294
)
294295

295-
AC_MSG_NOTICE([Configuration Summary:
296+
AC_MSG_NOTICE([Configuration Summary:
296297
Operating System: ${host_os}
297298
Target directory: ${prefix}
298299
License : LGPL only

src/http_request.cpp

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const char http_request::EMPTY[] = "";
3232

3333
struct arguments_accumulator {
3434
unescaper_ptr unescaper;
35-
http::arg_map* arguments;
35+
std::map<std::string, std::vector<std::string>, http::arg_comparator>* arguments;
3636
};
3737

3838
void http_request::set_method(const std::string& method) {
@@ -104,35 +104,63 @@ const http::header_view_map http_request::get_cookies() const {
104104
return get_headerlike_values(MHD_COOKIE_KIND);
105105
}
106106

107-
std::string_view http_request::get_arg(std::string_view key) const {
108-
std::map<std::string, std::string>::const_iterator it = cache->unescaped_args.find(std::string(key));
107+
void http_request::populate_args() const {
108+
if (!cache->unescaped_args.empty()) {
109+
return;
110+
}
111+
arguments_accumulator aa;
112+
aa.unescaper = unescaper;
113+
aa.arguments = &cache->unescaped_args;
114+
MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, reinterpret_cast<void*>(&aa));
115+
}
116+
117+
http_arg_value http_request::get_arg(std::string_view key) const {
118+
populate_args();
109119

120+
auto it = cache->unescaped_args.find(key);
110121
if (it != cache->unescaped_args.end()) {
111-
return it->second;
122+
http_arg_value arg;
123+
arg.values.reserve(it->second.size());
124+
for (const auto& value : it->second) {
125+
arg.values.push_back(value);
126+
}
127+
return arg;
112128
}
113-
114-
return get_connection_value(key, MHD_GET_ARGUMENT_KIND);
129+
return http_arg_value();
115130
}
116131

117-
const http::arg_view_map http_request::get_args() const {
118-
http::arg_view_map arguments;
132+
std::string_view http_request::get_arg_flat(std::string_view key) const {
133+
auto const it = cache->unescaped_args.find(key);
119134

120-
if (!cache->unescaped_args.empty()) {
121-
arguments.insert(cache->unescaped_args.begin(), cache->unescaped_args.end());
122-
return arguments;
135+
if (it != cache->unescaped_args.end()) {
136+
return it->second[0];
123137
}
124138

125-
arguments_accumulator aa;
126-
aa.unescaper = unescaper;
127-
aa.arguments = &cache->unescaped_args;
128-
129-
MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, reinterpret_cast<void*>(&aa));
139+
return get_connection_value(key, MHD_GET_ARGUMENT_KIND);
140+
}
130141

131-
arguments.insert(cache->unescaped_args.begin(), cache->unescaped_args.end());
142+
const http::arg_view_map http_request::get_args() const {
143+
populate_args();
132144

145+
http::arg_view_map arguments;
146+
for (const auto& [key, value] : cache->unescaped_args) {
147+
auto& arg_values = arguments[key];
148+
for (const auto& v : value) {
149+
arg_values.values.push_back(v);
150+
}
151+
}
133152
return arguments;
134153
}
135154

155+
const std::map<std::string_view, std::string_view, http::arg_comparator> http_request::get_args_flat() const {
156+
populate_args();
157+
std::map<std::string_view, std::string_view, http::arg_comparator> ret;
158+
for (const auto&[key, val] : cache->unescaped_args) {
159+
ret[key] = val[0];
160+
}
161+
return ret;
162+
}
163+
136164
http::file_info& http_request::get_or_create_file_info(const std::string& key, const std::string& upload_file_name) {
137165
return files[key][upload_file_name];
138166
}
@@ -155,7 +183,7 @@ MHD_Result http_request::build_request_args(void *cls, enum MHD_ValueKind kind,
155183
std::string value = ((arg_value == nullptr) ? "" : arg_value);
156184

157185
http::base_unescaper(&value, aa->unescaper);
158-
(*aa->arguments)[key] = value;
186+
(*aa->arguments)[key].push_back(value);
159187
return MHD_YES;
160188
}
161189

src/http_utils.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,7 @@ const std::string load_file(const std::string& filename) {
510510
}
511511
}
512512

513-
template<typename map_t>
514-
void dump_map(std::ostream& os, const std::string& prefix, const map_t& map) {
513+
void dump_header_map(std::ostream& os, const std::string& prefix, const http::header_view_map &map) {
515514
auto it = map.begin();
516515
auto end = map.end();
517516

@@ -524,12 +523,23 @@ void dump_map(std::ostream& os, const std::string& prefix, const map_t& map) {
524523
}
525524
}
526525

527-
void dump_header_map(std::ostream& os, const std::string& prefix, const http::header_view_map &map) {
528-
dump_map<http::header_view_map>(os, prefix, map);
529-
}
530-
531526
void dump_arg_map(std::ostream& os, const std::string& prefix, const http::arg_view_map &map) {
532-
dump_map<http::arg_view_map>(os, prefix, map);
527+
auto it = map.begin();
528+
auto end = map.end();
529+
530+
if (map.size()) {
531+
os << " " << prefix << " [";
532+
for (; it != end; ++it) {
533+
os << (*it).first << ":[";
534+
std::string sep = "";
535+
for (const auto& v : it->second.values) {
536+
os << sep << "\"" << v << "\"";
537+
sep = ", ";
538+
}
539+
os << "] ";
540+
}
541+
os << "]" << std::endl;
542+
}
533543
}
534544

535545
size_t base_unescaper(std::string* s, unescaper_ptr unescaper) {

src/httpserver.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "httpserver/deferred_response.hpp"
2828
#include "httpserver/digest_auth_fail_response.hpp"
2929
#include "httpserver/file_response.hpp"
30+
#include "httpserver/http_arg_value.hpp"
3031
#include "httpserver/http_request.hpp"
3132
#include "httpserver/http_resource.hpp"
3233
#include "httpserver/http_response.hpp"

src/httpserver/http_arg_value.hpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011-2019 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION)
22+
#error "Only <httpserver.hpp> or <httpserverpp> can be included directly."
23+
#endif
24+
25+
#ifndef SRC_HTTPSERVER_HTTP_ARG_VALUE_HPP_
26+
#define SRC_HTTPSERVER_HTTP_ARG_VALUE_HPP_
27+
28+
#include <string>
29+
#include <string_view>
30+
#include <vector>
31+
32+
namespace httpserver {
33+
34+
class http_arg_value {
35+
public:
36+
std::string_view get_flat_value() const {
37+
return values.empty() ? "" : values[0];
38+
}
39+
40+
std::vector<std::string_view> get_all_values() const {
41+
return values;
42+
}
43+
44+
operator std::string() const {
45+
return std::string(get_flat_value());
46+
}
47+
48+
operator std::string_view() const {
49+
return get_flat_value();
50+
}
51+
52+
operator std::vector<std::string>() const {
53+
std::vector<std::string> result;
54+
for (auto const & value : values) {
55+
result.push_back(std::string(value));
56+
}
57+
return result;
58+
}
59+
60+
std::vector<std::string_view> values;
61+
};
62+
63+
} // end namespace httpserver
64+
65+
#endif // SRC_HTTPSERVER_HTTP_ARG_VALUE_HPP_

src/httpserver/http_request.hpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <utility>
4141
#include <vector>
4242

43+
#include "httpserver/http_arg_value.hpp"
4344
#include "httpserver/http_utils.hpp"
4445
#include "httpserver/file_info.hpp"
4546

@@ -134,11 +135,17 @@ class http_request {
134135

135136
/**
136137
* Method used to get all args passed with the request.
137-
* @param result a map<string, string> > that will be filled with all args
138138
* @result the size of the map
139139
**/
140140
const http::arg_view_map get_args() const;
141141

142+
/**
143+
* Method used to get all args passed with the request. If any key has multiple
144+
* values, one value is chosen and returned.
145+
* @result the size of the map
146+
**/
147+
const std::map<std::string_view, std::string_view, http::arg_comparator> get_args_flat() const;
148+
142149
/**
143150
* Method to get or create a file info struct in the map if the provided filename is already in the map
144151
* return the exiting file info struct, otherwise create one in the map and return it.
@@ -174,9 +181,17 @@ class http_request {
174181
/**
175182
* Method used to get a specific argument passed with the request.
176183
* @param ket the specific argument to get the value from
184+
* @return the value(s) of the arg.
185+
**/
186+
http_arg_value get_arg(std::string_view key) const;
187+
188+
/**
189+
* Method used to get a specific argument passed with the request.
190+
* If the arg key has more than one value, only one is returned.
191+
* @param ket the specific argument to get the value from
177192
* @return the value of the arg.
178193
**/
179-
std::string_view get_arg(std::string_view key) const;
194+
std::string_view get_arg_flat(std::string_view key) const;
180195

181196
/**
182197
* Method used to get the content of the request.
@@ -298,7 +313,7 @@ class http_request {
298313
* @param value The value assumed by the argument
299314
**/
300315
void set_arg(const std::string& key, const std::string& value) {
301-
cache->unescaped_args[key] = value.substr(0, content_size_limit);
316+
cache->unescaped_args[key].push_back(value.substr(0, content_size_limit));
302317
}
303318

304319
/**
@@ -308,9 +323,17 @@ class http_request {
308323
* @param size The size in number of char of the value parameter.
309324
**/
310325
void set_arg(const char* key, const char* value, size_t size) {
311-
cache->unescaped_args[key] = std::string(value, std::min(size, content_size_limit));
326+
cache->unescaped_args[key].push_back(std::string(value, std::min(size, content_size_limit)));
312327
}
313328

329+
/**
330+
* Method used to set an argument value by key. If a key already exists, overwrites it.
331+
* @param key The name identifying the argument
332+
* @param value The value assumed by the argument
333+
**/
334+
void set_arg_flat(const std::string& key, const std::string& value) {
335+
cache->unescaped_args[key] = { (value.substr(0, content_size_limit)) };
336+
}
314337
/**
315338
* Method used to set the content of the request
316339
* @param content The content to set.
@@ -366,9 +389,8 @@ class http_request {
366389
* @param args The args key-value map to set for the request.
367390
**/
368391
void set_args(const std::map<std::string, std::string>& args) {
369-
std::map<std::string, std::string>::const_iterator it;
370-
for (it = args.begin(); it != args.end(); ++it) {
371-
this->cache->unescaped_args[it->first] = it->second.substr(0, content_size_limit);
392+
for (auto const& [key, value] : args) {
393+
this->cache->unescaped_args[key].push_back(value.substr(0, content_size_limit));
372394
}
373395
}
374396

@@ -386,9 +408,11 @@ class http_request {
386408
std::string querystring;
387409
std::string requestor_ip;
388410
std::string digested_user;
389-
http::arg_map unescaped_args;
411+
std::map<std::string, std::vector<std::string>, http::arg_comparator> unescaped_args;
390412
};
391413
std::unique_ptr<http_request_data_cache> cache;
414+
// Populate the data cache unescaped_args
415+
void populate_args() const;
392416

393417
friend class webserver;
394418
friend struct details::modded_request;

src/httpserver/http_response.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <iosfwd>
2929
#include <map>
3030
#include <string>
31+
#include "httpserver/http_arg_value.hpp"
3132
#include "httpserver/http_utils.hpp"
3233

3334
struct MHD_Connection;

src/httpserver/http_utils.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
#include <string>
5757
#include <vector>
5858

59+
#include "httpserver/http_arg_value.hpp"
60+
5961
#define DEFAULT_MASK_VALUE 0xFFFF
6062

6163
#if MHD_VERSION < 0x00097002
@@ -299,6 +301,7 @@ class header_comparator {
299301
**/
300302
class arg_comparator {
301303
public:
304+
using is_transparent = std::true_type;
302305
/**
303306
* Operator used to compare strings.
304307
* @param first string
@@ -314,12 +317,19 @@ class arg_comparator {
314317
bool operator()(const std::string& x, const std::string& y) const {
315318
return operator()(std::string_view(x), std::string_view(y));
316319
}
320+
bool operator()(std::string_view x, const std::string& y) const {
321+
return operator()(x, std::string_view(y));
322+
}
323+
bool operator()(const std::string& x, std::string_view y) const {
324+
return operator()(std::string_view(x), std::string(y));
325+
}
317326
};
318327

319328
using header_map = std::map<std::string, std::string, http::header_comparator>;
320329
using header_view_map = std::map<std::string_view, std::string_view, http::header_comparator>;
321-
using arg_map = std::map<std::string, std::string, http::arg_comparator>;
322-
using arg_view_map = std::map<std::string_view, std::string_view, http::arg_comparator>;
330+
using arg_map = std::map<std::string, http_arg_value, http::arg_comparator>;
331+
using arg_view_map = std::map<std::string_view, http_arg_value, http::arg_comparator>;
332+
323333

324334
struct ip_representation {
325335
http_utils::IP_version_T ip_version;

0 commit comments

Comments
 (0)