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
20 changes: 10 additions & 10 deletions src/http_resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ namespace httpserver { class http_response; }
namespace httpserver {

// RESOURCE
void resource_init(std::map<std::string, bool>* allowed_methods) {
(*allowed_methods)[MHD_HTTP_METHOD_GET] = true;
(*allowed_methods)[MHD_HTTP_METHOD_POST] = true;
(*allowed_methods)[MHD_HTTP_METHOD_PUT] = true;
(*allowed_methods)[MHD_HTTP_METHOD_HEAD] = true;
(*allowed_methods)[MHD_HTTP_METHOD_DELETE] = true;
(*allowed_methods)[MHD_HTTP_METHOD_TRACE] = true;
(*allowed_methods)[MHD_HTTP_METHOD_CONNECT] = true;
(*allowed_methods)[MHD_HTTP_METHOD_OPTIONS] = true;
(*allowed_methods)[MHD_HTTP_METHOD_PATCH] = true;
void resource_init(std::map<std::string, bool>* method_state) {
(*method_state)[MHD_HTTP_METHOD_GET] = true;
(*method_state)[MHD_HTTP_METHOD_POST] = true;
(*method_state)[MHD_HTTP_METHOD_PUT] = true;
(*method_state)[MHD_HTTP_METHOD_HEAD] = true;
(*method_state)[MHD_HTTP_METHOD_DELETE] = true;
(*method_state)[MHD_HTTP_METHOD_TRACE] = true;
(*method_state)[MHD_HTTP_METHOD_CONNECT] = true;
(*method_state)[MHD_HTTP_METHOD_OPTIONS] = true;
(*method_state)[MHD_HTTP_METHOD_PATCH] = true;
}

namespace details {
Expand Down
39 changes: 28 additions & 11 deletions src/httpserver/http_resource.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>

namespace httpserver { class http_request; }
namespace httpserver { class http_response; }
Expand Down Expand Up @@ -149,8 +150,8 @@ class http_resource {
* @param allowed boolean indicating if the method is allowed or not
**/
void set_allowing(const std::string& method, bool allowed) {
if (allowed_methods.count(method)) {
allowed_methods[method] = allowed;
if (method_state.count(method)) {
method_state[method] = allowed;
}
}

Expand All @@ -159,8 +160,8 @@ class http_resource {
**/
void allow_all() {
std::map<std::string, bool>::iterator it;
for (it=allowed_methods.begin(); it != allowed_methods.end(); ++it) {
allowed_methods[(*it).first] = true;
for (it=method_state.begin(); it != method_state.end(); ++it) {
method_state[(*it).first] = true;
}
}

Expand All @@ -169,8 +170,8 @@ class http_resource {
**/
void disallow_all() {
std::map<std::string, bool>::iterator it;
for (it=allowed_methods.begin(); it != allowed_methods.end(); ++it) {
allowed_methods[(*it).first] = false;
for (it=method_state.begin(); it != method_state.end(); ++it) {
method_state[(*it).first] = false;
}
}

Expand All @@ -180,25 +181,41 @@ class http_resource {
* @return true if the method is allowed
**/
bool is_allowed(const std::string& method) {
if (allowed_methods.count(method)) {
return allowed_methods[method];
if (method_state.count(method)) {
return method_state[method];
} else {
#ifdef DEBUG
std::map<std::string, bool>::iterator it;
for (it = allowed_methods.begin(); it != allowed_methods.end(); ++it) {
for (it = method_state.begin(); it != method_state.end(); ++it) {
std::cout << (*it).first << " -> " << (*it).second << std::endl;
}
#endif // DEBUG
return false;
}
}

/**
* Method used to return a list of currently allowed HTTP methods for this resource
* @return vector of strings
**/
std::vector<std::string> get_allowed_methods() {
std::vector<std::string> allowed_methods;

for (auto it = method_state.cbegin(); it != method_state.cend(); ++it) {
if ( (*it).second ) {
allowed_methods.push_back((*it).first);
}
}

return allowed_methods;
}

protected:
/**
* Constructor of the class
**/
http_resource() {
resource_init(&allowed_methods);
resource_init(&method_state);
}

/**
Expand All @@ -212,7 +229,7 @@ class http_resource {
private:
friend class webserver;
friend void resource_init(std::map<std::string, bool>* res);
std::map<std::string, bool> allowed_methods;
std::map<std::string, bool> method_state;
};

} // namespace httpserver
Expand Down
9 changes: 9 additions & 0 deletions src/webserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,15 @@ MHD_Result webserver::finalize_answer(MHD_Connection* connection, struct details
}
} else {
mr->dhrs = method_not_allowed_page(mr);

vector<string> allowed_methods = hrm->get_allowed_methods();
if (allowed_methods.size() > 0) {
string header_value = allowed_methods[0];
for (auto it = allowed_methods.cbegin() + 1; it != allowed_methods.cend(); ++it) {
header_value += ", " + (*it);
}
mr->dhrs->with_header(http_utils::http_header_allow, header_value);
}
}
} catch(const std::exception& e) {
mr->dhrs = internal_error_page(mr);
Expand Down
3 changes: 2 additions & 1 deletion test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
LDADD = $(top_builddir)/src/libhttpserver.la
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
METASOURCES = AUTO
check_PROGRAMS = basic http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred
check_PROGRAMS = basic http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource

MOSTLYCLEANFILES = *.gcda *.gcno *.gcov

Expand All @@ -33,6 +33,7 @@ http_utils_SOURCES = unit/http_utils_test.cpp
string_utilities_SOURCES = unit/string_utilities_test.cpp
http_endpoint_SOURCES = unit/http_endpoint_test.cpp
nodelay_SOURCES = integ/nodelay.cpp
http_resource_SOURCES = unit/http_resource_test.cpp

noinst_HEADERS = littletest.hpp
AM_CXXFLAGS += -lcurl -Wall -fPIC
Expand Down
29 changes: 28 additions & 1 deletion test/integ/basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_header)
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerfunc);
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &ss);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &ss);
res = curl_easy_perform(curl);
LT_ASSERT_EQ(res, 0);
LT_CHECK_EQ(s, "OK");
Expand Down Expand Up @@ -1021,6 +1021,33 @@ LT_BEGIN_AUTO_TEST(basic_suite, url_with_regex_like_pieces)
curl_easy_cleanup(curl);
LT_END_AUTO_TEST(url_with_regex_like_pieces)

LT_BEGIN_AUTO_TEST(basic_suite, method_not_allowed_header)
simple_resource resource;
resource.disallow_all();
resource.set_allowing("POST", true);
resource.set_allowing("HEAD", true);
ws->register_resource("base", &resource);
curl_global_init(CURL_GLOBAL_ALL);
string s;
map<string, string> ss;
CURL *curl = curl_easy_init();
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base");
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerfunc);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &ss);
res = curl_easy_perform(curl);
LT_ASSERT_EQ(res, 0);
int64_t http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
LT_ASSERT_EQ(http_code, 405);
// elements in http_resource::method_state are sorted (std::map)
LT_CHECK_EQ(ss["Allow"], "HEAD, POST");
curl_easy_cleanup(curl);
LT_END_AUTO_TEST(method_not_allowed_header)

LT_BEGIN_AUTO_TEST_ENV()
AUTORUN_TESTS()
LT_END_AUTO_TEST_ENV()
93 changes: 93 additions & 0 deletions test/unit/http_resource_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
This file is part of libhttpserver
Copyright (C) 2021 Alexander Dahl

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
*/

#include <microhttpd.h>

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include "httpserver.hpp"
#include "littletest.hpp"

using std::shared_ptr;
using std::sort;
using std::string;
using std::vector;

using httpserver::http_request;
using httpserver::http_resource;
using httpserver::http_response;
using httpserver::string_response;

class simple_resource : public http_resource {
public:
const shared_ptr<http_response> render_GET(const http_request&) {
return shared_ptr<string_response>(new string_response("OK"));
}
};

LT_BEGIN_SUITE(http_resource_suite)
void set_up() {
}

void tear_down() {
}
LT_END_SUITE(http_resource_suite)

LT_BEGIN_AUTO_TEST(http_resource_suite, disallow_all_methods)
simple_resource sr;
sr.disallow_all();
auto allowed_methods = sr.get_allowed_methods();
LT_CHECK_EQ(allowed_methods.size(), 0);
LT_END_AUTO_TEST(disallow_all_methods)

LT_BEGIN_AUTO_TEST(http_resource_suite, allow_some_methods)
simple_resource sr;
sr.disallow_all();
sr.set_allowing(MHD_HTTP_METHOD_GET, true);
sr.set_allowing(MHD_HTTP_METHOD_POST, true);
auto allowed_methods = sr.get_allowed_methods();
LT_CHECK_EQ(allowed_methods.size(), 2);
// elements in http_resource::method_state are sorted (std::map)
vector<string> some_methods{MHD_HTTP_METHOD_GET, MHD_HTTP_METHOD_POST};
sort(some_methods.begin(), some_methods.end());
LT_CHECK_COLLECTIONS_EQ(allowed_methods.cbegin(), allowed_methods.cend(),
some_methods.cbegin())
LT_END_AUTO_TEST(allow_some_methods)

LT_BEGIN_AUTO_TEST(http_resource_suite, allow_all_methods)
simple_resource sr;
sr.allow_all();
auto allowed_methods = sr.get_allowed_methods();
// elements in http_resource::method_state are sorted (std::map)
vector<string> all_methods{MHD_HTTP_METHOD_GET, MHD_HTTP_METHOD_POST,
MHD_HTTP_METHOD_PUT, MHD_HTTP_METHOD_HEAD, MHD_HTTP_METHOD_DELETE,
MHD_HTTP_METHOD_TRACE, MHD_HTTP_METHOD_CONNECT,
MHD_HTTP_METHOD_OPTIONS, MHD_HTTP_METHOD_PATCH};
sort(all_methods.begin(), all_methods.end());
LT_CHECK_COLLECTIONS_EQ(allowed_methods.cbegin(), allowed_methods.cend(),
all_methods.cbegin())
LT_END_AUTO_TEST(allow_all_methods)

LT_BEGIN_AUTO_TEST_ENV()
AUTORUN_TESTS()
LT_END_AUTO_TEST_ENV()