Arduino DLNA Server
Loading...
Searching...
No Matches
HttpServer.h
Go to the documentation of this file.
1#pragma once
2
3#include <WiFi.h>
4#include <stdlib.h>
5
6#include "Client.h"
7#include "HardwareSerial.h"
8#include "HttpChunkWriter.h"
9#include "HttpHeader.h"
11#include "HttpRequestRewrite.h"
12#include "HttpTunnel.h"
13#include "Server.h"
14#include "basic/List.h"
15
16namespace tiny_dlna {
17
25 public:
26 HttpServer(WiFiServer& server, int bufferSize = 1024) {
27 DlnaLogger.log(DlnaLogLevel::Info, "HttpServer");
28 this->server_ptr = &server;
29 this->buffer.resize(bufferSize);
30 }
31
33 DlnaLogger.log(DlnaLogLevel::Info, "~HttpServer");
34 handler_collection.clear();
35 request_header.clear(false);
36 reply_header.clear(false);
37 rewrite_collection.clear();
38 }
39
41 IPAddress& localIP() {
42 static IPAddress address;
43 address = WiFi.localIP();
44 return address;
45 }
46
49 bool begin(int port, const char* ssid, const char* password) {
50 if (WiFi.status() != WL_CONNECTED && ssid != nullptr &&
51 password != nullptr) {
52 WiFi.begin(ssid, password);
53 while (WiFi.status() != WL_CONNECTED) {
54 delay(500);
55 Serial.print(".");
56 }
57
58 Serial.println();
59 Serial.print("Started Server at ");
60 Serial.print(WiFi.localIP());
61 Serial.print(":");
62 Serial.println(port);
63 }
64 return begin(port);
65 }
66
68 bool begin(int port) {
69 DlnaLogger.log(DlnaLogLevel::Info, "HttpServer begin at port %d", port);
70 is_active = true;
71 server_ptr->begin(port);
72 return true;
73 }
74
76 void end() {
77 DlnaLogger.log(DlnaLogLevel::Info, "HttpServer %s", "stop");
78 is_active = false;
79 }
80
82 void rewrite(const char* from, const char* to) {
83 DlnaLogger.log(DlnaLogLevel::Info, "Rewriting %s to %s", from, to);
84 HttpRequestRewrite* line = new HttpRequestRewrite(from, to);
85 rewrite_collection.push_back(line);
86 }
87
89 void on(const char* url, TinyMethodID method, web_callback_fn fn,
90 void* ctx[] = nullptr, int ctxCount = 0) {
91 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
93 hl->path = url;
94 hl->fn = fn;
95 hl->method = method;
96 // hl->context = ctx;
97 memmove(hl->context, ctx, ctxCount * sizeof(void*));
98 hl->contextCount = ctxCount;
99 addHandler(hl);
100 }
101
103 void on(const char* url, TinyMethodID method, const char* mime,
104 web_callback_fn fn) {
105 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
107 hl->path = url;
108 hl->fn = fn;
109 hl->method = method;
110 hl->mime = mime;
111 addHandler(hl);
112 }
113
115 void on(const char* url, TinyMethodID method, const char* mime,
116 const char* result) {
117 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
118
119 auto lambda = [](HttpServer* server_ptr, const char* requestPath,
121 DlnaLogger.log(DlnaLogLevel::Info, "on-strings %s", "lambda");
122 if (hl->contextCount < 2) {
123 DlnaLogger.log(DlnaLogLevel::Error, "The context is not available");
124 return;
125 }
126 const char* mime = (const char*)hl->context[0];
127 const char* msg = (const char*)hl->context[1];
128 server_ptr->reply(mime, msg, 200);
129 };
131 hl->context[0] = (void*)mime;
132 hl->context[1] = (void*)result;
133 hl->path = url;
134 hl->fn = lambda;
135 hl->method = method;
136 addHandler(hl);
137 }
138
140 void on(const char* url, TinyMethodID method, const char* mime,
141 const uint8_t* data, int len) {
142 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
143
144 auto lambda = [](HttpServer* server_ptr, const char* requestPath,
146 DlnaLogger.log(DlnaLogLevel::Info, "on-strings %s", "lambda");
147 if (hl->contextCount < 3) {
148 DlnaLogger.log(DlnaLogLevel::Error, "The context is not available");
149 return;
150 }
151 const char* mime = static_cast<char*>(hl->context[0]);
152 const uint8_t* data = static_cast<uint8_t*>(hl->context[1]);
153 int* p_len = (int*)hl->context[2];
154 int len = *p_len;
155 DlnaLogger.log(DlnaLogLevel::Debug, "Mime %d - Len: %d", mime, len);
156 server_ptr->reply(mime, data, len, 200);
157 };
159 hl->context[0] = (void*)mime;
160 hl->context[1] = (void*)data;
161 hl->context[2] = new int(len);
162 hl->path = url;
163 hl->fn = lambda;
164 hl->method = method;
165 addHandler(hl);
166 }
167
169 void on(const char* url, TinyMethodID method, Url& redirect) {
170 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
171 auto lambda = [](HttpServer* server_ptr, const char* requestPath,
173 if (hl->contextCount < 1) {
174 DlnaLogger.log(DlnaLogLevel::Error, "The context is not available");
175 return;
176 }
177 DlnaLogger.log(DlnaLogLevel::Info, "on-redirect %s", "lambda");
179 Url* url = static_cast<Url*>(hl->context[0]);
180 reply_header.setValues(301, "Moved");
181 reply_header.put(LOCATION, url->url());
182 reply_header.put("X-Forwarded-Host", (const char*)hl->context[1]);
183 reply_header.write(server_ptr->client());
184 server_ptr->endClient();
185 };
186
188 const char* lh = localHost();
189 hl->context[0] = new Url(redirect);
190 hl->context[1] = (void*)lh;
191 hl->path = url;
192 hl->fn = lambda;
193 hl->method = method;
194 addHandler(hl);
195 }
196
198 void on(const char* url, TinyMethodID method, HttpTunnel& tunnel) {
199 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", url);
200
201 auto lambda = [](HttpServer* server_ptr, const char* requestPath,
203 DlnaLogger.log(DlnaLogLevel::Info, "on-HttpTunnel %s", "lambda");
204 HttpTunnel* p_tunnel = static_cast<HttpTunnel*>(hl->context[0]);
205 if (p_tunnel == nullptr) {
206 DlnaLogger.log(DlnaLogLevel::Error, "p_tunnel is null");
207 server_ptr->replyNotFound();
208 return;
209 }
210 const char* mime = hl->mime;
211 // execute T_GET request
212 Stream* p_in = p_tunnel->get();
213 if (p_in == nullptr) {
214 DlnaLogger.log(DlnaLogLevel::Error, "p_in is null");
215 server_ptr->replyNotFound();
216 return;
217 }
218 const char* content_len = p_tunnel->request().reply().get(CONTENT_LENGTH);
219 StrView content_len_str{content_len};
220 // provide result
221 server_ptr->reply(mime, *p_in, content_len_str.toInt());
222 };
223
225 hl->path = url;
226 hl->method = method;
227 hl->mime = tunnel.mime();
228 hl->context[0] = &tunnel;
229 hl->fn = lambda;
230 addHandler(hl);
231 }
232
235 bool onRequest(const char* path) {
236 DlnaLogger.log(DlnaLogLevel::Info, "Serving at %s", path);
237
238 bool result = false;
239 // check in registered handlers
240 StrView pathStr = StrView(path);
241 for (auto handler_line_ptr : handler_collection) {
242 DlnaLogger.log(DlnaLogLevel::Info, "onRequest: %s vs: %s %s %s",
243 path,
244 nullstr(handler_line_ptr->path.c_str()),
245 methods[handler_line_ptr->method],
246 nullstr(handler_line_ptr->mime));
247
248 if (pathStr.matches(handler_line_ptr->path.c_str()) &&
249 request_header.method() == handler_line_ptr->method &&
250 matchesMime(handler_line_ptr->mime, request_header.accept())) {
251 // call registed handler function
252 DlnaLogger.log(DlnaLogLevel::Info, "onRequest %s", "->found",
253 nullstr(handler_line_ptr->path.c_str()));
254 handler_line_ptr->fn(this, path, handler_line_ptr);
255 result = true;
256 break;
257 }
258 }
259
260 if (!result) {
261 DlnaLogger.log(DlnaLogLevel::Error, "Request %s not available", path);
262 }
263
264 return result;
265 }
266
268 void replyChunked(const char* contentType, Stream& inputStream,
269 int status = 200, const char* msg = SUCCESS) {
270 replyChunked(contentType, status, msg);
271 HttpChunkWriter chunk_writer;
272 while (inputStream.available()) {
273 int len = inputStream.readBytes(buffer.data(), buffer.size());
274 chunk_writer.writeChunk(*client_ptr, (const char*)buffer.data(), len);
275 }
276 // final chunk
277 chunk_writer.writeEnd(*client_ptr);
278 endClient();
279 }
280
282 void replyChunked(const char* contentType, int status = 200,
283 const char* msg = SUCCESS) {
284 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "replyChunked");
285 reply_header.setValues(status, msg);
287 reply_header.put(CONTENT_TYPE, contentType);
289 reply_header.write(this->client());
290 }
291
293 void reply(const char* contentType, Stream& inputStream, int size,
294 int status = 200, const char* msg = SUCCESS) {
295 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "stream");
296 reply_header.setValues(status, msg);
298 reply_header.put(CONTENT_TYPE, contentType);
300 reply_header.write(this->client());
301
302 while (inputStream.available()) {
303 int len = inputStream.readBytes(buffer.data(), buffer.size());
304 int written = client_ptr->write((const uint8_t*)buffer.data(), len);
305 }
306 // inputStream.close();
307 endClient();
308 }
309
311 void reply(const char* contentType, void (*callback)(Stream& out),
312 int status = 200, const char* msg = SUCCESS) {
313 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "callback");
314 reply_header.setValues(status, msg);
315 reply_header.put(CONTENT_TYPE, contentType);
317 reply_header.write(this->client());
318 callback(*client_ptr);
319 // inputStream.close();
320 endClient();
321 }
322
324 void reply(const char* contentType, void (*callback)(Print& out),
325 int status = 200, const char* msg = SUCCESS) {
326 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "callback");
327 reply_header.setValues(status, msg);
328 reply_header.put(CONTENT_TYPE, contentType);
330 reply_header.write(this->client());
331 callback(*client_ptr);
332 // inputStream.close();
333 endClient();
334 }
335
337 void reply(const char* contentType, const char* str, int status = 200,
338 const char* msg = SUCCESS) {
339 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "str");
340 int len = strlen(str);
341 reply_header.setValues(status, msg);
343 reply_header.put(CONTENT_TYPE, contentType);
345 reply_header.write(this->client());
346 client_ptr->write((const uint8_t*)str, len);
347 endClient();
348 }
349
350 void reply(const char* contentType, const uint8_t* str, int len,
351 int status = 200, const char* msg = SUCCESS) {
352 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "str");
353 reply_header.setValues(status, msg);
355 reply_header.put(CONTENT_TYPE, contentType);
357 reply_header.write(this->client());
358 client_ptr->write((const uint8_t*)str, len);
359 endClient();
360 }
361
363 void replyOK() { reply(200, SUCCESS); }
364
367 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "404");
368 reply(404, "Page Not Found");
369 }
370
372 void reply(int status, const char* msg) {
373 DlnaLogger.log(DlnaLogLevel::Info, "reply %d", status);
374 reply_header.setValues(status, msg);
375 reply_header.write(this->client());
376 endClient();
377 }
378
381
384
386 void endClient() {
387 DlnaLogger.log(DlnaLogLevel::Info, "HttpServer %s", "endClient");
388 client_ptr->flush();
389 client_ptr->stop();
390 }
391
393 void crlf() {
394 client_ptr->print("\r\n");
395 client_ptr->flush();
396 }
397
399 void addHandler(HttpRequestHandlerLine* handlerLinePtr) {
400 handler_collection.push_back(handlerLinePtr);
401 }
402
404 bool doLoop() { return copy(); }
405
407 bool copy() {
408 bool result = false;
409 // get the actual client_ptr
410 if (is_active) {
411 WiFiClient client = server_ptr->accept();
412 if (client.connected()) {
413 DlnaLogger.log(DlnaLogLevel::Info, "doLoop->hasClient");
415
416 // process the new client with standard functionality
417 if (client.available() > 5) {
419 }
420
421 result = true;
422 } else {
423 // give other tasks a chance
424 delay(no_connect_delay);
425 // DlnaLogger.log(DlnaLogLevel::Debug, "HttpServer no client available");
426 }
427 } else {
428 DlnaLogger.log(DlnaLogLevel::Warning, "HttpServer inactive");
429 }
430 return result;
431 }
432
434 Client& client() { return *client_ptr; }
435
437 operator bool() { return is_active; }
438
440 const char* localHost() {
441 if (local_host == nullptr) {
442 local_host = toStr(WiFi.localIP());
443 }
444 return local_host;
445 }
446
447 void setNoConnectDelay(int delay) { no_connect_delay = delay; }
448
451 uint8_t buffer[1024];
452 Str result;
453 while(client_ptr->available()) {
454 int len = client_ptr->readBytes(buffer, sizeof(buffer));
455 result.add((const uint8_t*)buffer, len);
456 }
457 result.add("\0");
458 return result;
459 }
460
461 protected:
462 // data
466 // List<Extension*> extension_collection;
468 Client* client_ptr = nullptr;
469 WiFiServer* server_ptr = nullptr;
472 const char* local_host = nullptr;
474
476 const char* nullstr(const char* in) { return in == nullptr ? "" : in; }
477
478 // process a full request and send the reply
480 DlnaLogger.log(DlnaLogLevel::Info, "processRequest");
481 request_header.read(this->client());
482 // provide reply with empty header
483 reply_header.clear();
484 // determine the path
485 const char* path = request_header.urlPath();
486 path = resolveRewrite(path);
487 bool processed = onRequest(path);
488 if (!processed) {
490 }
491 }
492
495 const char* resolveRewrite(const char* from) {
496 for (auto i = rewrite_collection.begin(); i != rewrite_collection.end();
497 ++i) {
499 if (rewrite->from.matches(from)) {
500 return rewrite->to.c_str();
501 }
502 }
503 return from;
504 }
505
508 bool matchesMime(const char* handler_mime, const char* request_mime) {
509 if (handler_mime == nullptr || request_mime == nullptr) {
510 return true;
511 }
512 bool result = StrView(request_mime).contains(handler_mime);
513 return result;
514 }
515};
516
517} // namespace tiny_dlna
Writes the data chunked to the actual client.
Definition: HttpChunkWriter.h:12
int writeChunk(Client &client, const char *str, int len, const char *str1=nullptr, int len1=0)
Definition: HttpChunkWriter.h:14
void writeEnd(Client &client)
Definition: HttpChunkWriter.h:31
const char * accept()
Definition: HttpHeader.h:223
void read(Client &in)
Definition: HttpHeader.h:235
HttpHeader & clear(bool activeFlag=true)
clears the data - usually we do not delete but we just set the active flag
Definition: HttpHeader.h:84
TinyMethodID method()
Definition: HttpHeader.h:221
const char * urlPath()
Definition: HttpHeader.h:217
HttpHeader & put(const char *key, const char *value)
Definition: HttpHeader.h:103
void write(Client &out)
Definition: HttpHeader.h:268
const char * get(const char *key)
Definition: HttpHeader.h:163
Reading and Writing of Http Replys.
Definition: HttpHeader.h:413
void setValues(int statusCode, const char *msg="", const char *protocol=nullptr)
Definition: HttpHeader.h:416
Used to register and process callbacks.
Definition: HttpRequestHandlerLine.h:19
web_callback_fn fn
Definition: HttpRequestHandlerLine.h:38
Str path
Definition: HttpRequestHandlerLine.h:36
TinyMethodID method
Definition: HttpRequestHandlerLine.h:35
void ** context
Definition: HttpRequestHandlerLine.h:39
const char * mime
Definition: HttpRequestHandlerLine.h:37
int contextCount
Definition: HttpRequestHandlerLine.h:40
Reading and writing of Http Requests.
Definition: HttpHeader.h:359
Object which information about the rewrite rule.
Definition: HttpRequestRewrite.h:11
virtual HttpReplyHeader & reply()
Definition: HttpRequest.h:116
A Simple Header only implementation of Http Server that allows the registration of callback functions...
Definition: HttpServer.h:24
bool is_active
Definition: HttpServer.h:470
const char * localHost()
Determines the local ip address.
Definition: HttpServer.h:440
bool begin(int port, const char *ssid, const char *password)
Definition: HttpServer.h:49
void setNoConnectDelay(int delay)
Definition: HttpServer.h:447
Client * client_ptr
Definition: HttpServer.h:468
bool doLoop()
Legacy method: same as copy();.
Definition: HttpServer.h:404
Vector< char > buffer
Definition: HttpServer.h:471
void on(const char *url, TinyMethodID method, web_callback_fn fn, void *ctx[]=nullptr, int ctxCount=0)
register a generic handler
Definition: HttpServer.h:89
void replyOK()
write OK reply with 200 SUCCESS
Definition: HttpServer.h:363
List< HttpRequestRewrite * > rewrite_collection
Definition: HttpServer.h:467
bool copy()
Call this method from your loop!
Definition: HttpServer.h:407
void on(const char *url, TinyMethodID method, Url &redirect)
register a redirection
Definition: HttpServer.h:169
Client & client()
Provides the current client.
Definition: HttpServer.h:434
void endClient()
closes the connection to the current client_ptr
Definition: HttpServer.h:386
List< HttpRequestHandlerLine * > handler_collection
Definition: HttpServer.h:465
bool onRequest(const char *path)
Definition: HttpServer.h:235
const char * local_host
Definition: HttpServer.h:472
void reply(const char *contentType, void(*callback)(Print &out), int status=200, const char *msg=SUCCESS)
write reply - using callback that writes to stream
Definition: HttpServer.h:324
bool matchesMime(const char *handler_mime, const char *request_mime)
Definition: HttpServer.h:508
const char * nullstr(const char *in)
Converts null to an empty string.
Definition: HttpServer.h:476
Str contentStr()
converts the client content to a string
Definition: HttpServer.h:450
void processRequest()
Definition: HttpServer.h:479
void reply(const char *contentType, Stream &inputStream, int size, int status=200, const char *msg=SUCCESS)
write reply - copies data from input stream with header size
Definition: HttpServer.h:293
HttpRequestHeader request_header
Definition: HttpServer.h:463
WiFiServer * server_ptr
Definition: HttpServer.h:469
void addHandler(HttpRequestHandlerLine *handlerLinePtr)
adds a new handler
Definition: HttpServer.h:399
IPAddress & localIP()
Provides the local ip address.
Definition: HttpServer.h:41
HttpRequestHeader & requestHeader()
provides the request header
Definition: HttpServer.h:380
void on(const char *url, TinyMethodID method, const char *mime, const char *result)
register a handler which provides the indicated string
Definition: HttpServer.h:115
void on(const char *url, TinyMethodID method, HttpTunnel &tunnel)
register a redirection
Definition: HttpServer.h:198
void reply(int status, const char *msg)
Writes the status and message to the reply.
Definition: HttpServer.h:372
HttpReplyHeader reply_header
Definition: HttpServer.h:464
void end()
stops the server_ptr
Definition: HttpServer.h:76
void replyNotFound()
write 404 reply
Definition: HttpServer.h:366
void crlf()
print a CR LF
Definition: HttpServer.h:393
void reply(const char *contentType, const char *str, int status=200, const char *msg=SUCCESS)
write reply - string with header size
Definition: HttpServer.h:337
void rewrite(const char *from, const char *to)
adds a rewrite rule
Definition: HttpServer.h:82
void reply(const char *contentType, void(*callback)(Stream &out), int status=200, const char *msg=SUCCESS)
write reply - using callback that writes to stream
Definition: HttpServer.h:311
int no_connect_delay
Definition: HttpServer.h:473
void replyChunked(const char *contentType, int status=200, const char *msg=SUCCESS)
start of chunked reply: use HttpChunkWriter to provde the data
Definition: HttpServer.h:282
void reply(const char *contentType, const uint8_t *str, int len, int status=200, const char *msg=SUCCESS)
Definition: HttpServer.h:350
void replyChunked(const char *contentType, Stream &inputStream, int status=200, const char *msg=SUCCESS)
chunked reply with data from an input stream
Definition: HttpServer.h:268
HttpServer(WiFiServer &server, int bufferSize=1024)
Definition: HttpServer.h:26
void on(const char *url, TinyMethodID method, const char *mime, const uint8_t *data, int len)
register a handler which provides the indicated string
Definition: HttpServer.h:140
const char * resolveRewrite(const char *from)
Definition: HttpServer.h:495
~HttpServer()
Definition: HttpServer.h:32
void on(const char *url, TinyMethodID method, const char *mime, web_callback_fn fn)
register a handler with mime
Definition: HttpServer.h:103
bool begin(int port)
Starts the server on the indicated port.
Definition: HttpServer.h:68
HttpReplyHeader & replyHeader()
provides the reply header
Definition: HttpServer.h:383
Forwards a request to a destination URL and provides a pointer to the result stream.
Definition: HttpTunnel.h:12
const char * mime()
Definition: HttpTunnel.h:31
DLNAHttpRequest & request()
Definition: HttpTunnel.h:29
Stream * get()
Executes the get request.
Definition: HttpTunnel.h:21
Double linked list.
Definition: List.h:19
void log(DlnaLogLevel current_level, const char *fmt...)
Print log message.
Definition: Logger.h:40
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:19
virtual void add(int value)
adds a int value
Definition: StrView.h:129
virtual bool matches(const char *pattern)
Definition: StrView.h:208
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: StrView.h:285
String implementation which keeps the data on the heap. We grow the allocated memory only if the copy...
Definition: Str.h:22
URL parser which breaks a full url string up into its individual parts.
Definition: Url.h:18
const char * url()
Definition: Url.h:43
Vector implementation which provides the most important methods as defined by std::vector....
Definition: Vector.h:21
bool resize(int newSize, T value)
Definition: Vector.h:251
int size()
Definition: Vector.h:167
T * data()
Definition: Vector.h:298
Definition: Allocator.h:6
const char * CONTENT_TYPE
Definition: HttpHeader.h:16
const char * CON_KEEP_ALIVE
Definition: HttpHeader.h:20
void(* web_callback_fn)(HttpServer *server, const char *requestPath, HttpRequestHandlerLine *handlerLine)
Definition: HttpRequestHandlerLine.h:12
const char * methods[]
Definition: HttpHeader.h:48
const char * CONTENT_LENGTH
Definition: HttpHeader.h:17
TinyMethodID
Definition: HttpHeader.h:35
const char * TRANSFER_ENCODING
Definition: HttpHeader.h:21
const char * SUCCESS
Definition: HttpHeader.h:25
LoggerClass DlnaLogger
Definition: Logger.cpp:5
const char * CHUNKED
Definition: HttpHeader.h:22
const char * CONNECTION
Definition: HttpHeader.h:18
const char * LOCATION
Definition: HttpHeader.h:32