148 lines
3.1 KiB
C++
148 lines
3.1 KiB
C++
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
|
|
#include <esp_http_server.h>
|
|
|
|
#include <util/inplace_function.h>
|
|
|
|
|
|
#define RESPONSE_BUFFER_LEN 512
|
|
|
|
//
|
|
//
|
|
// Types
|
|
//
|
|
//
|
|
|
|
|
|
namespace http {
|
|
|
|
|
|
enum class Method { get = HTTP_GET, post = HTTP_POST };
|
|
|
|
struct response_t {
|
|
response_t() = default;
|
|
response_t(const char* str) {
|
|
std::size_t respLen =
|
|
std::min(strlen(str), static_cast<unsigned>(RESPONSE_BUFFER_LEN));
|
|
std::copy(str, str + respLen, text.begin());
|
|
}
|
|
|
|
std::array<char, RESPONSE_BUFFER_LEN> text = {0};
|
|
};
|
|
|
|
struct handler_t {
|
|
Method method;
|
|
const char* path;
|
|
inplace_function<response_t()> handler;
|
|
};
|
|
|
|
struct server_settings_t {
|
|
int port = 80;
|
|
inplace_function<void()> on_error = [] {};
|
|
inplace_function<void()> on_not_found = [] {};
|
|
};
|
|
|
|
|
|
} // namespace http
|
|
|
|
|
|
//
|
|
//
|
|
// Implementation details
|
|
//
|
|
//
|
|
|
|
|
|
namespace http::detail {
|
|
|
|
|
|
inline auto& get_server() {
|
|
static httpd_handle_t server = nullptr;
|
|
return server;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
inline auto& get_handler_storage() {
|
|
static std::array<inplace_function<response_t()>, N> handlers;
|
|
return handlers;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
inline auto& get_esp_uri_storage() {
|
|
static std::array<httpd_uri_t, N> uris;
|
|
return uris;
|
|
}
|
|
|
|
|
|
// TODO: Currently, the set handler is only valid for GET requests
|
|
template <std::size_t N, std::size_t I>
|
|
inline void set_handlers_impl(const handler_t (&handlers)[N]) {
|
|
auto& uriStorage = get_esp_uri_storage<N>();
|
|
|
|
uriStorage[I] = httpd_uri_t{
|
|
.uri = handlers[I].path,
|
|
.method = static_cast<httpd_method_t>(handlers[I].method),
|
|
.handler =
|
|
[](httpd_req_t* req) {
|
|
auto resp = get_handler_storage<N>()[I]();
|
|
httpd_resp_send(req, resp.text.data(), HTTPD_RESP_USE_STRLEN);
|
|
return ESP_OK;
|
|
},
|
|
.user_ctx = nullptr,
|
|
};
|
|
|
|
httpd_register_uri_handler(get_server(), &(uriStorage[I]));
|
|
|
|
if constexpr (I < N - 1) set_handlers_impl<N, I + 1>(handlers);
|
|
}
|
|
|
|
|
|
} // namespace http::detail
|
|
|
|
|
|
//
|
|
//
|
|
// Public API
|
|
//
|
|
//
|
|
|
|
|
|
namespace http {
|
|
|
|
|
|
// TODO: Give request information to handlers
|
|
template <std::size_t N>
|
|
inline void set_handlers(const handler_t (&handlers)[N]) {
|
|
auto& handlerStorage = detail::get_handler_storage<N>();
|
|
std::transform(handlers, handlers + N, handlerStorage.begin(),
|
|
[](const auto& handler) {
|
|
return handler.handler;
|
|
});
|
|
|
|
detail::set_handlers_impl<N, 0>(handlers);
|
|
}
|
|
|
|
inline void start_server(server_settings_t settings) {
|
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
config.server_port = settings.port;
|
|
|
|
if (httpd_start(&detail::get_server(), &config) != ESP_OK) {
|
|
throw std::runtime_error("Failed to start server");
|
|
}
|
|
|
|
// TODO: Set error and not found handlers
|
|
}
|
|
|
|
|
|
inline void stop_server() {
|
|
if (httpd_stop(detail::get_server()) != ESP_OK) {
|
|
throw std::runtime_error("Failed to stop server");
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace http
|