#pragma once #include #include #include #include #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(RESPONSE_BUFFER_LEN)); std::copy(str, str + respLen, text.begin()); } std::array text = {0}; }; struct handler_t { Method method; const char* path; inplace_function handler; }; struct server_settings_t { int port = 80; inplace_function on_error = [] {}; inplace_function on_not_found = [] {}; }; } // namespace http // // // Implementation details // // namespace http::detail { inline auto& get_server() { static httpd_handle_t server = nullptr; return server; } template inline auto& get_handler_storage() { static std::array, N> handlers; return handlers; } template inline auto& get_esp_uri_storage() { static std::array uris; return uris; } // TODO: Currently, the set handler is only valid for GET requests template inline void set_handlers_impl(const handler_t (&handlers)[N]) { auto& uriStorage = get_esp_uri_storage(); uriStorage[I] = httpd_uri_t{ .uri = handlers[I].path, .method = static_cast(handlers[I].method), .handler = [](httpd_req_t* req) { auto resp = get_handler_storage()[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(handlers); } } // namespace http::detail // // // Public API // // namespace http { // TODO: Give request information to handlers template inline void set_handlers(const handler_t (&handlers)[N]) { auto& handlerStorage = detail::get_handler_storage(); std::transform(handlers, handlers + N, handlerStorage.begin(), [](const auto& handler) { return handler.handler; }); detail::set_handlers_impl(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