Firmware  0.0.0
Loading...
Searching...
No Matches
endpoints.hpp
Go to the documentation of this file.
1// Copyright (C) 2025 Vincent Hamp
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
21
22#pragma once
23
24#include <esp_http_server.h>
25#include <concepts>
26#include <functional>
27#include <map>
28#include <memory>
29#include <string>
30#include <vector>
31#include <ztl/fail.hpp>
32#include "log.h"
33#include "message.hpp"
34#include "request.hpp"
35#include "response.hpp"
36#include "utility.hpp"
37
38namespace http {
39
41class Endpoints {
42 using key_type = httpd_uri_t;
43
45 using sync_mapped_type = std::vector<std::function<Response(Request const&)>>;
46
48 using async_mapped_type = std::vector<std::function<esp_err_t(Message&)>>;
49
50public:
52 template<typename T, typename F>
53 void subscribe(key_type const& key, std::shared_ptr<T> t, F&& f) {
54 if constexpr (std::invocable<typename signature<F>::type, Request const&>)
55 _sync_map[key].push_back([t, f](auto&&... args) {
56 return std::invoke(f, *t, std::forward<decltype(args)>(args)...);
57 });
58 else if constexpr (std::invocable<typename signature<F>::type, Message&>)
59 _async_map[key].push_back([t, f](auto&&... args) {
60 return std::invoke(f, *t, std::forward<decltype(args)>(args)...);
61 });
62 else ztl::fail();
63 }
64
65protected:
67 Response syncResponse(httpd_req_t* req) {
68 static constexpr auto chunk_size{16384uz};
69
70 auto const key{req2key(req)};
71 auto const it{_sync_map.find(key)};
72 if (it == cend(_sync_map))
73 return std::unexpected<std::string>{"501 Not Implemented"};
74
75 // Request body can be red in chunks which avoids triggering the servers
76 // receive timeout
77 Request r{.uri = std::string(req->uri),
78 .body = std::string(req->content_len, '\0')};
79 int bytes_red{};
80 while (bytes_red < req->content_len) {
81 if (auto const tmp{
82 httpd_req_recv(req, data(r.body) + bytes_red, chunk_size)};
83 tmp > 0)
84 bytes_red += tmp;
85 else return std::unexpected<std::string>{"500 Internal Server Error"};
86 }
87
89 return it->second[0uz](r);
90 }
91
93 esp_err_t asyncResponse(httpd_req_t* req) {
94 auto const key{req2key(req)};
95 auto const it{_async_map.find(key)};
96 if (it == cend(_async_map)) return ESP_FAIL;
97 httpd_ws_frame_t frame{};
98 if (httpd_ws_recv_frame(req, &frame, 0uz)) return ESP_FAIL;
99
100 // WebSocket frame must be red in one go
101 Message msg{.sock_fd = httpd_req_to_sockfd(req),
102 .type = frame.type,
103 .payload = std::vector<uint8_t>(frame.len)};
104 if (frame.len) {
105 frame.payload = data(msg.payload);
106 if (httpd_ws_recv_frame(req, &frame, frame.len)) return ESP_FAIL;
107 }
108
109 return it->second[0uz](msg);
110 }
111
112private:
114 httpd_uri_t req2key(httpd_req_t* req) const {
115 return {.uri = req->uri,
116 .method = static_cast<httpd_method_t>(req->method)};
117 }
118
120 struct key_compare {
121 bool operator()(key_type const& lhs, key_type const& rhs) const {
122 return lhs.method != rhs.method
123 ? lhs.method < rhs.method
124 : strncmp(lhs.uri,
125 rhs.uri,
126 std::min(strlen(lhs.uri), strlen(rhs.uri))) < 0;
127 }
128 };
129
130 std::map<key_type, sync_mapped_type, key_compare> _sync_map;
131 std::map<key_type, async_mapped_type, key_compare> _async_map;
132};
133
134} // namespace http
Definition endpoints.hpp:41
std::vector< std::function< esp_err_t(Message &)> > async_mapped_type
Mapped type for WebSockets.
Definition endpoints.hpp:48
httpd_uri_t key_type
Definition endpoints.hpp:42
std::map< key_type, async_mapped_type, key_compare > _async_map
Definition endpoints.hpp:131
void subscribe(key_type const &key, std::shared_ptr< T > t, F &&f)
Definition endpoints.hpp:53
esp_err_t asyncResponse(httpd_req_t *req)
Definition endpoints.hpp:93
std::map< key_type, sync_mapped_type, key_compare > _sync_map
Definition endpoints.hpp:130
std::vector< std::function< Response(Request const &)> > sync_mapped_type
Mapped type for HTTP requests.
Definition endpoints.hpp:45
httpd_uri_t req2key(httpd_req_t *req) const
Definition endpoints.hpp:114
Response syncResponse(httpd_req_t *req)
Definition endpoints.hpp:67
Log macros without TAG parameter.
HTTP websocket message.
Definition config.hpp:299
std::expected< std::string, std::string > Response
Definition response.hpp:29
HTTP request.
HTTP response.
Definition endpoints.hpp:120
bool operator()(key_type const &lhs, key_type const &rhs) const
Definition endpoints.hpp:121
Definition message.hpp:29
Definition request.hpp:28
Utility functions.