AsyncFw 1.2
Async Framework is c++ runtime with timers, poll notifiers, sockets, coroutines, etc.
 
Loading...
Searching...
No Matches
HttpServer.h
1/*
2Copyright (c) 2026 Alexandr Kuzmuk
3
4This file is part of the AsyncFw project. Licensed under the MIT License.
5See {Link: LICENSE file https://mit-license.org} in the project root for full license information.
6*/
7
8#pragma once
9
10#include <map>
11#include <memory>
12#include <functional>
13
14#include "../core/TlsContext.h"
15#include "HttpSocket.h"
16#include "Instance.h"
17
18class WebSocket;
19namespace AsyncFw {
20using namespace AsyncFw;
23class HttpServer {
24 friend LogStream &operator<<(LogStream &, const HttpServer &);
25 struct Private;
26
27public:
28 class Response;
29
30protected:
31 class TcpSocket : public AsyncFw::HttpSocket {
32 friend class HttpServer;
33
34 public:
35 TcpSocket(HttpServer *);
36 ~TcpSocket();
37
38 protected:
39 void readEvent() override;
40
41 private:
42 Response *response = nullptr;
43 WebSocket *ws_ = nullptr;
44 HttpServer *server_;
45 };
46
47public:
48 class Response {
49 friend class HttpServer;
50
51 public:
52 enum class StatusCode {
53 Continue = 100,
54 SwitchingProtocols = 101,
55 Processing = 102,
56 Ok = 200,
57 Created = 201,
58 Accepted = 202,
59 NonAuthoritativeInformation = 203,
60 NoContent = 204,
61 ResetContent = 205,
62 PartialContent = 206,
63 MultiStatus = 207,
64 AlreadyReported = 208,
65 IMUsed = 226,
66 MultipleChoices = 300,
67 MovedPermanently = 301,
68 Found = 302,
69 SeeOther = 303,
70 NotModified = 304,
71 UseProxy = 305,
72 TemporaryRedirect = 307,
73 PermanentRedirect = 308,
74 BadRequest = 400,
75 Unauthorized = 401,
76 PaymentRequired = 402,
77 Forbidden = 403,
78 NotFound = 404,
79 MethodNotAllowed = 405,
80 NotAcceptable = 406,
81 ProxyAuthenticationRequired = 407,
82 RequestTimeout = 408,
83 Conflict = 409,
84 Gone = 410,
85 LengthRequired = 411,
86 PreconditionFailed = 412,
87 PayloadTooLarge = 413,
88 UriTooLong = 414,
89 UnsupportedMediaType = 415,
90 RequestRangeNotSatisfiable = 416,
91 ExpectationFailed = 417,
92 ImATeapot = 418,
93 MisdirectedRequest = 421,
94 UnprocessableEntity = 422,
95 Locked = 423,
96 FailedDependency = 424,
97 UpgradeRequired = 426,
98 PreconditionRequired = 428,
99 TooManyRequests = 429,
100 RequestHeaderFieldsTooLarge = 431,
101 LoginTimeOut = 440,
102 UnavailableForLegalReasons = 451,
103 InternalServerError = 500,
104 NotImplemented = 501,
105 BadGateway = 502,
106 ServiceUnavailable = 503,
107 GatewayTimeout = 504,
108 HttpVersionNotSupported = 505,
109 VariantAlsoNegotiates = 506,
110 InsufficientStorage = 507,
111 LoopDetected = 508,
112 NotExtended = 510,
113 NetworkAuthenticationRequired = 511,
114 NetworkConnectTimeoutError = 599
115 };
116
117 void setMimeType(const std::string &mime) { mimeType_ = mime; }
118 std::string mimeType() const { return mimeType_; }
119 void addHeader(const std::string &ba) { additionalHeaders.push_back(ba); }
120 std::string header() const;
121
122 void destroy();
123 bool send();
124 void setContent(const AsyncFw::DataArray &);
125 void setContent(const std::vector<uint8_t> &);
126 void setStatusCode(const StatusCode &_statusCode) { statusCode_ = _statusCode; }
127 bool fail() { return socket_ == nullptr; }
128
129 Response::StatusCode statusCode() { return statusCode_; }
130 AsyncFw::DataArray content() { return content_; }
131 TcpSocket *socket() { return socket_; }
132
133 private:
134 std::vector<std::string> additionalHeaders;
135 mutable StatusCode statusCode_ = Response::StatusCode::Ok;
136 mutable int contentLength = 0;
137 mutable std::string mimeType_;
138 AsyncFw::DataArray content_;
139
140 std::string version = "1.1";
141 bool cors_headers_enabled = false;
142 mutable TcpSocket *socket_ = nullptr;
143 };
144
145 class Request {
146 friend class HttpServer;
147 struct Private;
148
149 public:
150 enum class Method {
151 Unknown = 0x0000,
152 Get = 0x0001,
153 Put = 0x0002,
154 Delete = 0x0004,
155 Post = 0x0008,
156 Head = 0x0010,
157 Options = 0x0020,
158 Patch = 0x0040,
159 Connect = 0x0080,
160 Trace = 0x0100,
161 //AnyKnown = Get | Put | Delete | Post | Head | Options | Patch | Connect | Trace,
162 };
163
164 Request(const std::string_view &);
165 virtual ~Request();
166
167 Method method() const { return method_; }
168 std::string methodName() const;
169 std::string path() const;
170 std::string heaaderItemValue(const std::string &) const;
171 std::string queryItemValue(const std::string &) const;
172
173 AsyncFw::DataArray content() const;
174 std::string peerAddress() const { return response_->socket_->peerAddress(); }
175
176 void setSocketData(const std::any &data) const { response_->socket_->data_ = data; }
177 std::any &socketData() const { return response_->socket_->data_; }
178 Response *response() const { return response_; }
179 bool switchingProtocols() const;
180
181 bool fail() const;
182
183 private:
184 Method method_;
185 Response *response_ = nullptr;
186 Private *private_;
187 };
188
189private:
190 class HttpRule {
191 friend class HttpServer;
192
193 public:
194 const Request::Method method;
195
196 private:
197 HttpRule(const Request::Method method, std::function<void(const Request &)> exec) : method(method), exec(exec) {}
198 std::function<void(const Request &)> exec;
199 };
200
201public:
202 using RulesMap = std::multimap<std::string, std::unique_ptr<HttpRule>>;
203
204 HttpServer(const std::string & = {});
205 virtual ~HttpServer();
206
207 template <typename A>
208 void addRoute(const std::string &url, Request::Method method, A action, const std::any &data = {}) {
209 addRule(url, method, [this, action, data](const Request &request) {
210 if (!peek || peek(request, data)) action(request);
211 });
212 if (findRule(url, Request::Method::Options) != rules.end()) return;
213 addRule(url, Request::Method::Options, [this, action, data](const Request &request) {
214 if (cors_request_enabled) request.response()->setStatusCode(Response::StatusCode::Ok);
215 else { request.response()->setStatusCode(Response::StatusCode::Forbidden); }
216 });
217 }
218 template <typename O, typename A>
219 void addRoute(const O object, const std::string &url, Request::Method method, A action, const std::any &data = {}) {
220 addRoute(url, method, [object, action](const Request &request) { return (object->*action)(request); }, data);
221 }
222
223 template <typename T>
224 void clearConnections(const T &_data) {
225 for (TcpSocket *socket : sockets) {
226 if (socket->data_.has_value() && _data == std::any_cast<T>(socket->data_)) disconnectFromHost(socket);
227 }
228 }
229 void clearConnections() {
230 for (TcpSocket *socket : sockets) disconnectFromHost(socket);
231 }
232 template <typename T>
233 int sendToWebSockets(const T &_data, const AsyncFw::DataArray &_da) {
235 int _r = 0;
236 for (TcpSocket *socket : sockets) {
237 if (!socket->ws_) continue;
238 if (socket->data_.has_value() && _data == std::any_cast<T>(socket->data_)) {
239 if (_f.empty()) {
240 int _s = makeWebSocketFrame(_da, &_f);
241 if (_s <= 0) return -1;
242 }
243 socket->write(_f);
244 ++_r;
245 }
246 }
247 return _r;
248 }
249 void sendToWebSockets(const std::string &);
250
251 void disconnectFromHost(TcpSocket *socket);
252 bool listen(uint16_t port);
253 void close();
254
255 uint16_t port();
256 bool hasTls();
257
258 void addRule(const std::string &, const Request::Method, std::function<void(const Request &)>);
259 bool execRule(const Request &);
260
261 void setTlsContext(const AsyncFw::TlsContext &);
262 void setEnableCorsRequests(bool);
263 void setPeek(std::function<bool(const Request &, const std::any &)> f) { peek = f; }
264
265 static HttpServer *instance() { return instance_.value; }
266 AsyncFw::FunctionConnectorProtected<HttpServer>::Connector<int, const std::string &, bool *> incoming {AsyncFw::AbstractFunctionConnector::SyncOnly};
267
268protected:
269 virtual void fileUploadProgress(TcpSocket *, int);
270 RulesMap rules;
271
272private:
273 bool incomingConnection(int, const std::string &);
274 void received(TcpSocket *, const std::string_view &);
275 int makeWebSocketFrame(const AsyncFw::DataArray &, AsyncFw::DataArray *);
276 RulesMap::iterator findRule(const std::string &, const Request::Method);
277 std::vector<TcpSocket *> sockets;
278 bool cors_request_enabled = true;
279 static inline Instance<HttpServer> instance_ {"HttpServer"};
280 std::function<bool(const Request &, std::any)> peek;
281 Private *private_;
282};
283} // namespace AsyncFw
The DataArray class.
Definition DataArray.h:20
The HttpSocket class.
Definition HttpSocket.h:18
The Instance class.
Definition Instance.h:40
The LogStream class.
Definition LogStream.h:44
The TlsContext class provides functionality for managing TLS certificates.
Definition TlsContext.h:24