Arduino DLNA Server
Loading...
Searching...
No Matches
HttpServer.h
Go to the documentation of this file.
1#pragma once
2
3#include <stdlib.h>
4
5#include "Client.h"
6#include "HardwareSerial.h"
7#include "HttpChunkWriter.h"
8#include "HttpClientHandler.h"
9#include "HttpHeader.h"
11#include "HttpRequestRewrite.h"
12#include "IHttpServer.h"
13#include "Server.h"
14#include "basic/IPAddressAndPort.h" // for toStr
15#include "basic/List.h"
16#include "basic/StrPrint.h"
18
19namespace tiny_dlna {
20
36template <typename ClientType, typename ServerType>
37class HttpServer : public IHttpServer {
38 public:
39 HttpServer(ServerType& server, int bufferSize = 1024) {
40 DlnaLogger.log(DlnaLogLevel::Info, "HttpServer");
41 this->server_ptr = &server;
42 client_handler.resize(bufferSize);
43 }
44
45 ~HttpServer() override {
46 DlnaLogger.log(DlnaLogLevel::Info, "~HttpServer");
47 handler_collection.clear();
50 rewrite_collection.clear();
51 }
52
54 IPAddress& localIP() override {
55 static IPAddress address;
56 address = WiFi.localIP();
57 return address;
58 }
59
61 bool begin() override {
62 DlnaLogger.log(DlnaLogLevel::Info, "HttpServer begin");
63 is_active = true;
64 server_ptr->begin();
65 return true;
66 }
67
69 void end() override {
70 DlnaLogger.log(DlnaLogLevel::Info, "HttpServer %s", "stop");
71 is_active = false;
72 }
73
75 void rewrite(const char* from, const char* to) override {
76 DlnaLogger.log(DlnaLogLevel::Info, "Rewriting %s to %s", from, to);
77 HttpRequestRewrite* line = new HttpRequestRewrite(from, to);
78 rewrite_collection.push_back(line);
79 }
80
82 void on(const char* url, TinyMethodID method, web_callback_fn fn,
83 void* ctx[] = nullptr, int ctxCount = 0) override {
84 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
86 hl->path = url;
87 hl->fn = fn;
88 hl->method = method;
89 // hl->context = ctx;
90 memmove(hl->context, ctx, ctxCount * sizeof(void*));
91 hl->contextCount = ctxCount;
92 addHandler(hl);
93 }
94
96 void on(const char* url, TinyMethodID method, const char* mime,
97 web_callback_fn fn) override {
98 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
100 hl->path = url;
101 hl->fn = fn;
102 hl->method = method;
103 hl->mime = mime;
104 addHandler(hl);
105 }
106
108 void on(const char* url, TinyMethodID method, const char* mime,
109 const char* result) override {
110 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
111
112 auto lambda = [](IClientHandler& handler, IHttpServer*, const char*,
114 DlnaLogger.log(DlnaLogLevel::Info, "on-strings %s", "lambda");
115 if (hl->contextCount < 2) {
116 DlnaLogger.log(DlnaLogLevel::Error, "The context is not available");
117 return;
118 }
119 const char* mime = (const char*)hl->context[0];
120 const char* msg = (const char*)hl->context[1];
121 handler.reply(mime, msg, 200);
122 };
124 hl->context[0] = (void*)mime;
125 hl->context[1] = (void*)result;
126 hl->path = url;
127 hl->fn = lambda;
128 hl->method = method;
129 addHandler(hl);
130 }
131
133 void on(const char* url, TinyMethodID method, const char* mime,
134 const uint8_t* data, int len) override {
135 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
136
137 auto lambda = [](IClientHandler& handler, IHttpServer*, const char*,
139 DlnaLogger.log(DlnaLogLevel::Info, "on-strings %s", "lambda");
140 if (hl->contextCount < 3) {
141 DlnaLogger.log(DlnaLogLevel::Error, "The context is not available");
142 return;
143 }
144 const char* mime = static_cast<char*>(hl->context[0]);
145 const uint8_t* data = static_cast<uint8_t*>(hl->context[1]);
146 int* p_len = (int*)hl->context[2];
147 int len = *p_len;
148 DlnaLogger.log(DlnaLogLevel::Debug, "Mime %d - Len: %d", mime, len);
149 handler.reply(mime, data, len, 200);
150 };
152 hl->context[0] = (void*)mime;
153 hl->context[1] = (void*)data;
154 hl->context[2] = new int(len);
155 hl->path = url;
156 hl->fn = lambda;
157 hl->method = method;
158 addHandler(hl);
159 }
160
162 void on(const char* url, TinyMethodID method, Url& redirect) override {
163 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
164 auto lambda = [](IClientHandler& handler, IHttpServer*, const char*,
166 if (hl->contextCount < 1) {
167 DlnaLogger.log(DlnaLogLevel::Error, "The context is not available");
168 return;
169 }
170 DlnaLogger.log(DlnaLogLevel::Info, "on-redirect %s", "lambda");
171 HttpReplyHeader reply_header;
172 Url* url = static_cast<Url*>(hl->context[0]);
173 reply_header.setValues(301, "Moved");
174 reply_header.put(LOCATION, url->url());
175 reply_header.put("X-Forwarded-Host", (const char*)hl->context[1]);
176 reply_header.write(*handler.client());
177 handler.endClient();
178 };
179
181 const char* lh = localHost();
182 hl->context[0] = new Url(redirect);
183 hl->context[1] = (void*)lh;
184 hl->path = url;
185 hl->fn = lambda;
186 hl->method = method;
187 addHandler(hl);
188 }
189
192 bool onRequest(const char* path) override {
193 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", path);
194
195 bool result = false;
196 // check in registered handlers
197 StrView pathStr = StrView(path);
198 pathStr.replace("//", "/"); // TODO investiage why we get //
199 for (auto handler_line_ptr : handler_collection) {
200 DlnaLogger.log(DlnaLogLevel::Debug, "onRequest: %s %s vs: %s %s %s", path,
202 nullstr(handler_line_ptr->path.c_str()),
203 methods[handler_line_ptr->method],
204 nullstr(handler_line_ptr->mime));
205
206 if (pathStr.matches(handler_line_ptr->path.c_str()) &&
207 client_handler.requestHeader().method() == handler_line_ptr->method &&
208 matchesMime(nullstr(handler_line_ptr->mime),
210 // call registed handler function
211 DlnaLogger.log(DlnaLogLevel::Debug, "onRequest %s", "->found",
212 nullstr(handler_line_ptr->path.c_str()));
213 handler_line_ptr->fn(client_handler, this, path, handler_line_ptr);
214 result = true;
215 break;
216 }
217 }
218
219 if (!result) {
220 DlnaLogger.log(DlnaLogLevel::Error, "Request %s not available", path);
221 }
222
223 return result;
224 }
225
227 void addHandler(HttpRequestHandlerLine* handlerLinePtr) {
228 handler_collection.push_back(handlerLinePtr);
229 }
230
232 bool doLoop() override { return copy(); }
233
235 bool copy() override {
236 if (!is_active) {
237 delay(no_connect_delay);
238 return false;
239 }
240
241 // Accept new client and add to list if connected
242 ClientType client = server_ptr->accept();
243 if (client.connected()) {
244 DlnaLogger.log(DlnaLogLevel::Info, "copy: accepted new client");
245 client.setTimeout(DLNA_HTTP_READ_TIMEOUT_MS);
246#ifdef ESP32
247 client.setNoDelay(true); // disables Nagle
248#endif
249 open_clients.push_back(client);
250 }
251
252 // Stop when nothing to process
253 if (open_clients.empty()) {
254 // delay(no_connect_delay);
255 return false;
256 }
257
261 delay(no_connect_delay);
262 return false;
263 }
264 }
265
266 ClientType* p_client = &(*current_client_iterator);
267 if (!p_client->connected()) {
268 DlnaLogger.log(DlnaLogLevel::Debug, "copy: removing disconnected client");
269 auto to_erase = current_client_iterator;
271 open_clients.erase(to_erase);
272 return false;
273 }
274 if (p_client->available() == 0) {
275 DlnaLogger.log(DlnaLogLevel::Debug,
276 "copy: no data available from client");
279 !open_clients.empty()) {
281 }
282 return false;
283 }
284 processRequest(p_client);
287 !open_clients.empty()) {
289 }
290
291 // cleanup clients
293 return true;
294 }
295
297 operator bool() override { return is_active; }
298
299 bool isActive() override { return is_active; }
300
302 const char* localHost() override {
303 if (local_host == nullptr) {
304 local_host = toStr(WiFi.localIP());
305 }
306 return local_host;
307 }
308
309 void setNoConnectDelay(int delay) override { no_connect_delay = delay; }
310
311 // /// converts the client content to a string
312 // Str contentStr(ClientType* client) {
313 // uint8_t buffer[1024];
314 // Str result;
315 // while (client->available()) {
316 // int len = client->readBytes(buffer, sizeof(buffer));
317 // result.add((const uint8_t*)buffer, len);
318 // }
319 // result.add("\0");
320 // return result;
321 // }
322
323 // const char* urlPath() { return client_handler.requestHeader().urlPath(); }
324
326 void setReference(void* reference) override { ref = reference; }
327
329 void* getReference() override { return ref; }
330
331 protected:
332 // data
339 ServerType* server_ptr = nullptr;
341 const char* local_host = nullptr;
343 void* ref = nullptr;
344
345 /* Remove all closed/disconnected clients from the open_clients list.
346 * This also keeps `current_client_iterator` valid by advancing it to
347 * the next element when the erased element equals the current iterator.
348 */
350 auto it = open_clients.begin();
351 while (it != open_clients.end()) {
352 if ((!(*it).connected())) {
353 auto to_erase = it;
354 ++it;
355 open_clients.erase(to_erase);
356 if (current_client_iterator == to_erase) {
358 }
359 } else {
360 ++it;
361 }
362 }
363 if (open_clients.empty()) {
365 }
366 }
367
368 // Converts null to an empty string
369 const char* nullstr(const char* in) { return in == nullptr ? "" : in; }
370
371 // process a full request and send the reply
372 void processRequest(ClientType* p_client) {
373 DlnaLogger.log(DlnaLogLevel::Info, "processRequest");
374 client_handler.setClient(p_client);
376 const char* path = client_handler.requestHeader().urlPath();
377 path = resolveRewrite(path);
378 bool processed = onRequest(path);
379 if (!processed) {
381 }
382 }
385 const char* resolveRewrite(const char* from) {
386 for (auto i = rewrite_collection.begin(); i != rewrite_collection.end();
387 ++i) {
389 if (rewrite->from.matches(from)) {
390 return rewrite->to.c_str();
391 }
392 }
393 return from;
394 }
395
398 bool matchesMime(const char* handler_mime, const char* request_mime) {
399 DlnaLogger.log(DlnaLogLevel::Debug, "matchesMime: %s vs %s", handler_mime,
400 request_mime);
401 if (StrView(handler_mime).isEmpty() || StrView(request_mime).isEmpty()) {
402 return true;
403 }
404 bool result = StrView(request_mime).contains(handler_mime);
405 return result;
406 }
407};
408
410
411} // namespace tiny_dlna
Handles HTTP client connections and responses for the DLNA HTTP server.
Definition: HttpClientHandler.h:30
HttpRequestHeader & requestHeader()
Definition: HttpClientHandler.h:192
void readHttpHeader()
Reads the http header info from the client.
Definition: HttpClientHandler.h:42
HttpReplyHeader & replyHeader()
Definition: HttpClientHandler.h:194
void setClient(ClientT *client)
Definition: HttpClientHandler.h:39
void replyNotFound() override
Definition: HttpClientHandler.h:170
void resize(size_t newSize)
Definition: HttpClientHandler.h:197
const char * accept()
Definition: HttpHeader.h:221
HttpHeader & clear(bool activeFlag=true)
clears the data - usually we do not delete but we just set the active flag
Definition: HttpHeader.h:86
TinyMethodID method()
Definition: HttpHeader.h:219
const char * urlPath()
Definition: HttpHeader.h:215
HttpHeader & put(const char *key, const char *value)
Definition: HttpHeader.h:105
void write(Client &out)
Definition: HttpHeader.h:267
Reading and Writing of Http Replys.
Definition: HttpHeader.h:409
void setValues(int statusCode, const char *msg="", const char *protocol=nullptr)
Definition: HttpHeader.h:412
Used to register and process callbacks.
Definition: HttpRequestHandlerLine.h:12
web_callback_fn fn
Definition: HttpRequestHandlerLine.h:36
Str path
Definition: HttpRequestHandlerLine.h:34
TinyMethodID method
Definition: HttpRequestHandlerLine.h:33
void ** context
Definition: HttpRequestHandlerLine.h:37
const char * mime
Definition: HttpRequestHandlerLine.h:35
int contextCount
Definition: HttpRequestHandlerLine.h:38
Object which information about the rewrite rule.
Definition: HttpRequestRewrite.h:11
Header-only HTTP server wrapper that registers callback handlers.
Definition: HttpServer.h:37
ListLockFree< ClientType >::Iterator current_client_iterator
Definition: HttpServer.h:337
void setReference(void *reference) override
Definesa reference/context object.
Definition: HttpServer.h:326
void processRequest(ClientType *p_client)
Definition: HttpServer.h:372
ListLockFree< ClientType > open_clients
Definition: HttpServer.h:335
bool copy() override
Call this method from your loop!
Definition: HttpServer.h:235
void on(const char *url, TinyMethodID method, web_callback_fn fn, void *ctx[]=nullptr, int ctxCount=0) override
register a generic handler
Definition: HttpServer.h:82
void on(const char *url, TinyMethodID method, Url &redirect) override
register a redirection
Definition: HttpServer.h:162
bool isActive() override
Definition: HttpServer.h:299
void setNoConnectDelay(int delay) override
Definition: HttpServer.h:309
void removeClosedClients()
Definition: HttpServer.h:349
bool onRequest(const char *path) override
Definition: HttpServer.h:192
bool matchesMime(const char *handler_mime, const char *request_mime)
Definition: HttpServer.h:398
void on(const char *url, TinyMethodID method, const char *mime, const uint8_t *data, int len) override
register a handler which provides the indicated string
Definition: HttpServer.h:133
void on(const char *url, TinyMethodID method, const char *mime, const char *result) override
register a handler which provides the indicated string
Definition: HttpServer.h:108
HttpServer(ServerType &server, int bufferSize=1024)
Definition: HttpServer.h:39
int no_connect_delay
Definition: HttpServer.h:342
void rewrite(const char *from, const char *to) override
adds a rewrite rule
Definition: HttpServer.h:75
ServerType * server_ptr
Definition: HttpServer.h:339
bool doLoop() override
Legacy method: same as copy();.
Definition: HttpServer.h:232
bool is_active
Definition: HttpServer.h:340
~HttpServer() override
Definition: HttpServer.h:45
List< HttpRequestRewrite * > rewrite_collection
Definition: HttpServer.h:334
const char * nullstr(const char *in)
Definition: HttpServer.h:369
IPAddress & localIP() override
Provides the local ip address.
Definition: HttpServer.h:54
HttpClientHandler< ClientType > client_handler
Definition: HttpServer.h:336
void end() override
stops the server_ptr
Definition: HttpServer.h:69
const char * local_host
Definition: HttpServer.h:341
void addHandler(HttpRequestHandlerLine *handlerLinePtr)
adds a new handler
Definition: HttpServer.h:227
bool begin() override
Starts the server.
Definition: HttpServer.h:61
List< HttpRequestHandlerLine * > handler_collection
Definition: HttpServer.h:333
const char * resolveRewrite(const char *from)
Definition: HttpServer.h:385
void * ref
Definition: HttpServer.h:343
void * getReference() override
Provides access to a reference/context object.
Definition: HttpServer.h:329
void on(const char *url, TinyMethodID method, const char *mime, web_callback_fn fn) override
register a handler with mime
Definition: HttpServer.h:96
const char * localHost() override
Determines the local ip address.
Definition: HttpServer.h:302
Definition: IHttpServer.h:19
virtual void reply(const char *contentType, Stream &inputStream, int size, int status=200, const char *msg=SUCCESS)=0
virtual Client * client()=0
virtual void endClient()=0
Abstract interface for HTTP server functionality.
Definition: IHttpServer.h:50
Bidirectional iterator for ListLockFree.
Definition: ListLockFree.h:44
Lock-free double linked list using atomic operations.
Definition: ListLockFree.h:28
Iterator end()
Definition: ListLockFree.h:395
bool empty()
Definition: ListLockFree.h:415
Iterator begin()
Definition: ListLockFree.h:388
bool erase(Iterator it)
Definition: ListLockFree.h:357
bool push_back(const T &data)
Definition: ListLockFree.h:195
Double linked list.
Definition: List.h:19
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:18
virtual bool matches(const char *pattern)
Definition: StrView.h:207
virtual bool replace(const char *toReplace, const int replaced)
Replaces the first instance of toReplace with replaced.
Definition: StrView.h:395
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: StrView.h:284
URL parser which breaks a full url string up into its individual parts.
Definition: Url.h:18
const char * url()
Definition: Url.h:39
#define DLNA_HTTP_READ_TIMEOUT_MS
Define the default http request timeout.
Definition: dlna_config.h:20
Definition: Allocator.h:13
const char * methods[]
Definition: HttpHeader.h:50
TinyMethodID
Definition: HttpHeader.h:35
void(* web_callback_fn)(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *handlerLine)
Definition: IHttpServer.h:40
const char * LOCATION
Definition: HttpHeader.h:32