Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ AC_ARG_ENABLE([same-directory-build],
AC_MSG_RESULT([$samedirectory])

if test x"$samedirectory" = x"no"; then
if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then
if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then
AC_MSG_ERROR("you must configure in a separate build directory")
fi
fi
Expand Down Expand Up @@ -275,6 +275,7 @@ AC_SUBST(EXT_LIBS)
AC_CONFIG_FILES([test/test_content:test/test_content])
AC_CONFIG_FILES([test/test_content_2:test/test_content_2])
AC_CONFIG_FILES([test/test_content_empty:test/test_content_empty])
AC_CONFIG_FILES([test/test_content_large:test/test_content_large])
AC_CONFIG_FILES([test/cert.pem:test/cert.pem])
AC_CONFIG_FILES([test/key.pem:test/key.pem])
AC_CONFIG_FILES([test/test_root_ca.pem:test/test_root_ca.pem])
Expand All @@ -292,7 +293,7 @@ AC_OUTPUT(
examples/Makefile
)

AC_MSG_NOTICE([Configuration Summary:
AC_MSG_NOTICE([Configuration Summary:
Operating System: ${host_os}
Target directory: ${prefix}
License : LGPL only
Expand Down
64 changes: 46 additions & 18 deletions src/http_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const char http_request::EMPTY[] = "";

struct arguments_accumulator {
unescaper_ptr unescaper;
http::arg_map* arguments;
std::map<std::string, std::vector<std::string>, http::arg_comparator>* arguments;
};

void http_request::set_method(const std::string& method) {
Expand Down Expand Up @@ -104,35 +104,63 @@ const http::header_view_map http_request::get_cookies() const {
return get_headerlike_values(MHD_COOKIE_KIND);
}

std::string_view http_request::get_arg(std::string_view key) const {
std::map<std::string, std::string>::const_iterator it = cache->unescaped_args.find(std::string(key));
void http_request::populate_args() const {
if (!cache->unescaped_args.empty()) {
return;
}
arguments_accumulator aa;
aa.unescaper = unescaper;
aa.arguments = &cache->unescaped_args;
MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, reinterpret_cast<void*>(&aa));
}

http_arg_value http_request::get_arg(std::string_view key) const {
populate_args();

auto it = cache->unescaped_args.find(key);
if (it != cache->unescaped_args.end()) {
return it->second;
http_arg_value arg;
arg.values.reserve(it->second.size());
for (const auto& value : it->second) {
arg.values.push_back(value);
}
return arg;
}

return get_connection_value(key, MHD_GET_ARGUMENT_KIND);
return http_arg_value();
}

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

if (!cache->unescaped_args.empty()) {
arguments.insert(cache->unescaped_args.begin(), cache->unescaped_args.end());
return arguments;
if (it != cache->unescaped_args.end()) {
return it->second[0];
}

arguments_accumulator aa;
aa.unescaper = unescaper;
aa.arguments = &cache->unescaped_args;

MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, reinterpret_cast<void*>(&aa));
return get_connection_value(key, MHD_GET_ARGUMENT_KIND);
}

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

http::arg_view_map arguments;
for (const auto& [key, value] : cache->unescaped_args) {
auto& arg_values = arguments[key];
for (const auto& v : value) {
arg_values.values.push_back(v);
}
}
return arguments;
}

const std::map<std::string_view, std::string_view, http::arg_comparator> http_request::get_args_flat() const {
populate_args();
std::map<std::string_view, std::string_view, http::arg_comparator> ret;
for (const auto&[key, val] : cache->unescaped_args) {
ret[key] = val[0];
}
return ret;
}

http::file_info& http_request::get_or_create_file_info(const std::string& key, const std::string& upload_file_name) {
return files[key][upload_file_name];
}
Expand All @@ -155,7 +183,7 @@ 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;
(*aa->arguments)[key].push_back(value);
return MHD_YES;
}

Expand Down
24 changes: 17 additions & 7 deletions src/http_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,7 @@ const std::string load_file(const std::string& filename) {
}
}

template<typename map_t>
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) {
auto it = map.begin();
auto end = map.end();

Expand All @@ -524,12 +523,23 @@ 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<http::header_view_map>(os, prefix, map);
}

void dump_arg_map(std::ostream& os, const std::string& prefix, const http::arg_view_map &map) {
dump_map<http::arg_view_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 sep = "";
for (const auto& v : it->second.values) {
os << sep << "\"" << v << "\"";
sep = ", ";
}
os << "] ";
}
os << "]" << std::endl;
}
}

size_t base_unescaper(std::string* s, unescaper_ptr unescaper) {
Expand Down
1 change: 1 addition & 0 deletions src/httpserver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "httpserver/deferred_response.hpp"
#include "httpserver/digest_auth_fail_response.hpp"
#include "httpserver/file_response.hpp"
#include "httpserver/http_arg_value.hpp"
#include "httpserver/http_request.hpp"
#include "httpserver/http_resource.hpp"
#include "httpserver/http_response.hpp"
Expand Down
65 changes: 65 additions & 0 deletions src/httpserver/http_arg_value.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
This file is part of libhttpserver
Copyright (C) 2011-2019 Sebastiano Merlino

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
*/

#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION)
#error "Only <httpserver.hpp> or <httpserverpp> can be included directly."
#endif

#ifndef SRC_HTTPSERVER_HTTP_ARG_VALUE_HPP_
#define SRC_HTTPSERVER_HTTP_ARG_VALUE_HPP_

#include <string>
#include <string_view>
#include <vector>

namespace httpserver {

class http_arg_value {
public:
std::string_view get_flat_value() const {
return values.empty() ? "" : values[0];
}

std::vector<std::string_view> get_all_values() const {
return values;
}

operator std::string() const {
return std::string(get_flat_value());
}

operator std::string_view() const {
return get_flat_value();
}

operator std::vector<std::string>() const {
std::vector<std::string> result;
for (auto const & value : values) {
result.push_back(std::string(value));
}
return result;
}

std::vector<std::string_view> values;
};

} // end namespace httpserver

#endif // SRC_HTTPSERVER_HTTP_ARG_VALUE_HPP_
40 changes: 32 additions & 8 deletions src/httpserver/http_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <utility>
#include <vector>

#include "httpserver/http_arg_value.hpp"
#include "httpserver/http_utils.hpp"
#include "httpserver/file_info.hpp"

Expand Down Expand Up @@ -134,11 +135,17 @@ class http_request {

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

/**
* Method used to get all args passed with the request. If any key has multiple
* values, one value is chosen and returned.
* @result the size of the map
**/
const std::map<std::string_view, std::string_view, http::arg_comparator> get_args_flat() const;

/**
* Method to get or create a file info struct in the map if the provided filename is already in the map
* return the exiting file info struct, otherwise create one in the map and return it.
Expand Down Expand Up @@ -174,9 +181,17 @@ 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(s) of the arg.
**/
http_arg_value get_arg(std::string_view key) const;

/**
* Method used to get a specific argument passed with the request.
* If the arg key has more than one value, only one is returned.
* @param ket the specific argument to get the value from
* @return the value of the arg.
**/
std::string_view get_arg(std::string_view key) const;
std::string_view get_arg_flat(std::string_view key) const;

/**
* Method used to get the content of the request.
Expand Down Expand Up @@ -298,7 +313,7 @@ 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);
cache->unescaped_args[key].push_back(value.substr(0, content_size_limit));
}

/**
Expand All @@ -308,9 +323,17 @@ 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));
cache->unescaped_args[key].push_back(std::string(value, std::min(size, content_size_limit)));
}

/**
* Method used to set an argument value by key. If a key already exists, overwrites it.
* @param key The name identifying the argument
* @param value The value assumed by the argument
**/
void set_arg_flat(const std::string& key, const std::string& value) {
cache->unescaped_args[key] = { (value.substr(0, content_size_limit)) };
}
/**
* Method used to set the content of the request
* @param content The content to set.
Expand Down Expand Up @@ -366,9 +389,8 @@ class http_request {
* @param args The args key-value map to set for the request.
**/
void set_args(const std::map<std::string, std::string>& args) {
std::map<std::string, std::string>::const_iterator it;
for (it = args.begin(); it != args.end(); ++it) {
this->cache->unescaped_args[it->first] = it->second.substr(0, content_size_limit);
for (auto const& [key, value] : args) {
this->cache->unescaped_args[key].push_back(value.substr(0, content_size_limit));
}
}

Expand All @@ -386,9 +408,11 @@ class http_request {
std::string querystring;
std::string requestor_ip;
std::string digested_user;
http::arg_map unescaped_args;
std::map<std::string, std::vector<std::string>, http::arg_comparator> unescaped_args;
};
std::unique_ptr<http_request_data_cache> cache;
// Populate the data cache unescaped_args
void populate_args() const;

friend class webserver;
friend struct details::modded_request;
Expand Down
1 change: 1 addition & 0 deletions src/httpserver/http_response.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <iosfwd>
#include <map>
#include <string>
#include "httpserver/http_arg_value.hpp"
#include "httpserver/http_utils.hpp"

struct MHD_Connection;
Expand Down
14 changes: 12 additions & 2 deletions src/httpserver/http_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
#include <string>
#include <vector>

#include "httpserver/http_arg_value.hpp"

#define DEFAULT_MASK_VALUE 0xFFFF

#if MHD_VERSION < 0x00097002
Expand Down Expand Up @@ -299,6 +301,7 @@ class header_comparator {
**/
class arg_comparator {
public:
using is_transparent = std::true_type;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this doing? (I cannot find it used elsewhere)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required to activate heterogeneous lookup using this comparator. is_transparent just needs to be defined, it doesn't get referenced anywhere. From cppreference:

The member type is_transparent indicates to the caller that this function object is a transparent function object: it accepts arguments of arbitrary types and uses perfect forwarding, which avoids unnecessary copying and conversion when the function object is used in heterogeneous context, or with rvalue arguments. In particular, template functions such as std::set::find and std::set::lower_bound make use of this member type on their Compare types.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh cool. I learned something :). Thank you!

/**
* Operator used to compare strings.
* @param first string
Expand All @@ -314,12 +317,19 @@ class arg_comparator {
bool operator()(const std::string& x, const std::string& y) const {
return operator()(std::string_view(x), std::string_view(y));
}
bool operator()(std::string_view x, const std::string& y) const {
return operator()(x, std::string_view(y));
}
bool operator()(const std::string& x, std::string_view y) const {
return operator()(std::string_view(x), std::string(y));
}
};

using header_map = std::map<std::string, std::string, http::header_comparator>;
using header_view_map = std::map<std::string_view, std::string_view, http::header_comparator>;
using arg_map = std::map<std::string, std::string, http::arg_comparator>;
using arg_view_map = std::map<std::string_view, std::string_view, http::arg_comparator>;
using arg_map = std::map<std::string, http_arg_value, http::arg_comparator>;
using arg_view_map = std::map<std::string_view, http_arg_value, http::arg_comparator>;


struct ip_representation {
http_utils::IP_version_T ip_version;
Expand Down
Loading