Arduino DLNA Server
Loading...
Searching...
No Matches
DLNADevice.h
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4
5#include "basic/Icon.h"
6#include "basic/Printf.h"
7#include "basic/StrView.h"
8#include "basic/Url.h"
9#include "basic/Vector.h"
10#include "dlna/common/Action.h"
21#include "dlna/xml/XMLPrinter.h"
22#include "http/Http.h"
24
25namespace tiny_dlna {
26
46template <typename ClientType>
47class DLNADevice : public IDevice {
48 public:
50 // distribute initial subscription active flag to subscription manager
52 }
54 bool begin(DLNADeviceInfo& device, IUDPService& udp,
55 IHttpServer& server) override {
56 DlnaLogger.log(DlnaLogLevel::Info, "DLNADevice::begin");
57 server.setReference(this);
58 p_server = &server;
59 p_udp = &udp;
60 setDeviceInfo(device);
62
63 // check base url
64 const char* baseUrl = device.getBaseURL();
65 DlnaLogger.log(DlnaLogLevel::Info, "base URL: %s", baseUrl);
66
67 if (StrView(device.getBaseURL()).contains("localhost")) {
68 DlnaLogger.log(DlnaLogLevel::Error, "invalid base address: %s", baseUrl);
69 return false;
70 }
71
72 // setup device
73 device.setupServices(server, udp);
74
75 // ensure services reflect the current subscriptions-active flag
76 // (some callers rely on subscriptions being disabled by default)
78
79 // setup web server
80 if (!setupDLNAServer(server)) {
81 DlnaLogger.log(DlnaLogLevel::Error, "setupDLNAServer failed");
82 return false;
83 }
84
85 // start web server
86 Url url{baseUrl};
87 if (!p_server->begin()) {
88 DlnaLogger.log(DlnaLogLevel::Error, "Server failed");
89 return false;
90 }
91
92 // setup UDP
93 if (!p_udp->begin(DLNABroadcastAddress)) {
94 DlnaLogger.log(DlnaLogLevel::Error, "UDP begin failed");
95 return false;
96 }
97
98 // setup scheduler
99 if (!setupScheduler()) {
100 DlnaLogger.log(DlnaLogLevel::Error, "Scheduler failed");
101 return false;
102 }
103 // initialize scheduling timers so the scheduled tasks run immediately
104 uint64_t now = millis();
105 // set next timeouts to now so the first loop triggers the tasks
108
109 is_active = true;
110 DlnaLogger.log(DlnaLogLevel::Info, "Device successfully started");
111 return true;
112 }
113
115 return subscription_mgr;
116 }
117
119 void end() override {
120 DlnaLogger.log(DlnaLogLevel::Info, "DLNADevice::end");
121 p_server->end();
122
123 // send 3 bye messages
125 bye->repeat_ms = 800;
126 scheduler.add(bye);
127
128 // execute scheduler for 2 seconds
129 uint64_t end = millis() + 2000;
130 while (millis() < end) {
132 }
133
134 // end subscription manager
136
137 is_active = false;
138 }
139
142 bool loop(int loopAction = RUN_ALL) override {
143 if (!is_active) return false;
144 DlnaLogger.log(DlnaLogLevel::Debug, "loop");
145 // Platform-specific periodic diagnostics (e.g. ESP32 memory logging)
147 bool rc = false;
148
149 // handle server requests
150 if (loopAction & RUN_SERVER) {
151 rc = loopServer();
152 }
153
154 // Use millisecond-based intervals for scheduling so callers can set
155 // real-time intervals instead of loop-counts.
156 uint64_t now = millis();
157 if ((loopAction & RUN_UDP) && isSchedulerActive() &&
159 int count = loopUDPMessages();
160 if (count > 0) rc = true;
161 // schedule next run
163 }
164
165 // deliver any queued subscription notifications (if enabled)
166 if ((loopAction & RUN_SUBSCRIPTIONS) && isSubscriptionsActive() &&
168 int count = loopPublishSubscriptions();
169 if (count > 0) rc = true;
170 // schedule next run
172 }
173
174 // be nice, if we have other tasks
175 if (!rc) delay(DLNA_LOOP_DELAY_MS);
176 return true;
177 }
178
180 int loopPublishSubscriptions() { return subscription_mgr.publish(); }
181
183 bool loopServer() override {
184 DlnaLogger.log(DlnaLogLevel::Debug, "loopServer");
185 if (!is_active) return false;
186 bool rc = p_server->doLoop();
187 DlnaLogger.log(DlnaLogLevel::Debug, "server %s", rc ? "true" : "false");
188 return rc;
189 }
190
193 // process UDP requests
194 DlnaLogger.log(DlnaLogLevel::Debug, "loopUDPMessages");
195 RequestData req = p_udp->receive();
196 if (req) {
197 Schedule* schedule = parser.parse(*p_device_info, req);
198 if (schedule != nullptr) {
199 scheduler.add(schedule);
200 }
201 }
202
203 // execute scheduled udp replys
204 return scheduler.execute(*p_udp);
205 }
206
208 DLNAServiceInfo& getService(const char* id) override {
209 return p_device_info->getService(id);
210 }
211
213 DLNAServiceInfo& getServiceByAbbrev(const char* abbrev) override {
214 return p_device_info->getServiceByAbbrev(abbrev);
215 }
216
220 DLNAServiceInfo* getServiceByEventPath(const char* requestPath) override {
221 DlnaLogger.log(DlnaLogLevel::Debug, "getServiceByEventPath");
222 if (p_device_info == nullptr || requestPath == nullptr) return nullptr;
224 if (StrView(s.event_sub_url).equals(requestPath)) {
225 return &s;
226 }
227 }
228 return nullptr;
229 }
230
232 void addChange(const char* serviceAbbrev,
233 std::function<size_t(Print&, void*)> changeWriter,
234 void* ref) override {
235 DlnaLogger.log(DlnaLogLevel::Debug, "addChange");
236 // not started yet (begin() not called): nobody to notify
237 if (p_device_info == nullptr) return;
238 auto& mgr = getSubscriptionMgr();
239 DLNAServiceInfo& serviceInfo = getServiceByAbbrev(serviceAbbrev);
240 if (!serviceInfo) {
241 DlnaLogger.log(DlnaLogLevel::Warning,
242 "addChange: No service info available for %s",
243 serviceAbbrev);
244 return;
245 }
246 mgr.addChange(serviceInfo, changeWriter, ref);
247 }
248
251
253 void setSchedulerActive(bool flag) override { scheduler.setActive(flag); }
254
256 bool isSchedulerActive() override { return scheduler.isActive(); }
257
260 void setPostAliveRepeatMs(uint32_t ms) override { post_alive_repeat_ms = ms; }
261
263 void setSubscriptionsActive(bool flag) override {
265 if (p_device_info != nullptr) {
267 }
269 }
270
272 bool isSubscriptionsActive() const override {
274 }
275
277 static void parseActionRequest(IHttpServer* server, IClientHandler& client,
278 const char* requestPath,
280 ActionRequest& action) {
281 DlnaLogger.log(DlnaLogLevel::Info, "parseActionRequest");
282 auto start = millis();
283 // Entities are expanded per argument value (below) and not on the raw
284 // stream: otherwise escaped XML values (e.g. the DIDL-Lite in
285 // CurrentURIMetaData) would be parsed as nested elements.
287
288 Str outNodeName;
289 Vector<Str> outPath;
290 Str outText;
291 Str outAttributes;
292 bool is_attribute = false;
293 bool is_action = false;
294 Str actionName;
295 char buffer[XML_PARSER_BUFFER_SIZE];
296 // Client* client = server->client(); // No longer valid, use handler
297 // reference
298
299 auto* pClient = client.client();
300 const char* clen_str = client.requestHeader().get("Content-Length");
301 int remaining = -1;
302 if (clen_str != nullptr) {
303 char* endptr = nullptr;
304 long val = strtol(clen_str, &endptr, 10);
305 // only trust a header that actually parsed as a non-negative number;
306 // otherwise fall back to reading until the client stalls
307 if (endptr != clen_str && val >= 0) remaining = (int)val;
308 }
309 while (remaining != 0) {
310 size_t chunk = remaining < 0 || (size_t)remaining > sizeof(buffer)
311 ? sizeof(buffer)
312 : (size_t)remaining;
313 size_t len = pClient->readBytes(buffer, chunk);
314 if (len == 0) break;
315 if (remaining > 0) remaining -= (int)len;
316 xp.write((const uint8_t*)buffer, len);
317
318 while (xp.parse(outNodeName, outPath, outText, outAttributes)) {
319 if (is_attribute) {
320 const char* argName = outNodeName.c_str();
322 action.addArgument(argName, outText.c_str());
323 continue;
324 }
325 if (is_action) {
326 is_action = false;
327 is_attribute = true;
328 action.setAction(outNodeName.c_str());
329 DlnaLogger.log(DlnaLogLevel::Info, "action: %s", action.getAction());
330 continue;
331 }
332 // skip SOAP envelope wrappers (different clients use different
333 // namespace prefixes, e.g. s:Envelope, soap:Envelope, SOAP-ENV:Envelope)
334 if (outNodeName.endsWith("Envelope")) {
335 continue;
336 }
337 if (outNodeName.endsWith("Body")) {
338 is_action = true;
339 continue;
340 }
341 }
342 }
343 xp.end();
344 DlnaLogger.log(DlnaLogLevel::Info, "Parse took %d ms", millis() - start);
345 }
346
355 static size_t printReplyXML(
356 Print& out, const char* replyName, const char* serviceId,
357 std::function<size_t(Print&, void*)> valuesWriter = nullptr,
358 void* ref = nullptr) {
359 DlnaLogger.log(DlnaLogLevel::Debug, "printReplyXML");
360 XMLPrinter xp(out);
361 size_t result = 0;
362 result += xp.printNodeBegin(
363 "s:Envelope",
364 "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"");
365 result += xp.printNodeBegin("s:Body");
366 result +=
367 xp.printf("<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s:1\">\n",
368 replyName, serviceId);
369
370 // e.g.<u:return>Stop,Pause,Next,Seek</u:return> for
371 // QueryStateVariableResponse
372 if (valuesWriter) {
373 result += valuesWriter(out, ref);
374 }
375
376 result += xp.printf("</u:%s>\n", replyName);
377 result += xp.printNodeEnd("s:Body");
378 result += xp.printNodeEnd("s:Envelope");
379 return result;
380 }
385 static size_t replyGetProtocolInfo(Print& out, const char* source = "",
386 const char* sink = "") {
387 DlnaLogger.log(DlnaLogLevel::Debug, "replyGetProtocolInfo");
388 return printReplyXML(out, "GetProtocolInfoResponse", "ConnectionManager",
389 [source, sink](Print& o, void* ref) -> size_t {
390 (void)ref;
391 size_t written = 0;
392 written += o.print("<Source>");
393 written += o.print(StrView(source).c_str());
394 written += o.print("</Source>");
395 written += o.print("<Sink>");
396 written += o.print(StrView(sink).c_str());
397 written += o.print("</Sink>");
398 return written;
399 });
400 }
401
402 static size_t replyGetCurrentConnectionIDs(Print& out, const char* ids) {
403 DlnaLogger.log(DlnaLogLevel::Debug, "replyGetCurrentConnectionIDs");
404 return printReplyXML(out, "GetCurrentConnectionIDsResponse",
405 "ConnectionManager",
406 [ids](Print& o, void* ref) -> size_t {
407 (void)ref;
408 size_t written = 0;
409 // UPnP spec uses "CurrentConnectionIDs" as the
410 // response element name
411 written += o.print("<CurrentConnectionIDs>");
412 written += o.print(StrView(ids ? ids : "0").c_str());
413 written += o.print("</CurrentConnectionIDs>");
414 return written;
415 });
416 }
417
418 static size_t replyGetCurrentConnectionInfo(Print& out,
419 const char* protocolInfo,
420 const char* connectionID,
421 const char* direction) {
422 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscription");
423 return printReplyXML(
424 out, "GetCurrentConnectionInfoResponse", "ConnectionManager",
425 [protocolInfo, connectionID, direction](Print& o, void* ref) -> size_t {
426 (void)ref;
427 size_t written = 0;
428 // Write directly to out to avoid using a fixed-size intermediate
429 // buffer.
430 written += o.print("<RcsID>0</RcsID>");
431 written += o.print("<AVTransportID>0</AVTransportID>");
432 written += o.print("<ProtocolInfo>");
433 written += o.print(StrView(protocolInfo).c_str());
434 written += o.print("</ProtocolInfo>");
435 written += o.print("<PeerConnectionManager></PeerConnectionManager>");
436 written += o.print("<PeerConnectionID>");
437 written += o.print(StrView(connectionID).c_str());
438 written += o.print("</PeerConnectionID>");
439 written += o.print("<Direction>");
440 written += o.print(StrView(direction).c_str());
441 written += o.print("</Direction>");
442 written += o.print("<Status>OK</Status>");
443 return written;
444 });
445 }
446
448 static bool handleSubscription(IHttpServer* server, IClientHandler* client,
449 const char* requestPath,
451 bool& is_subscribe) {
452 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscription");
453 // logic separated and easier to test.
454 is_subscribe = false;
455 // Use client handler for request header and error reply
456 if (client) {
457 HttpRequestHeader& req = client->requestHeader();
458 if (req.method() == T_SUBSCRIBE) {
459 is_subscribe = true;
460 return handleSubscribe(client, server, requestPath, hl); // If needed
461 }
462 if (req.method() == T_UNSUBSCRIBE || req.method() == T_POST) {
463 return handleUnsubscribe(client, server, requestPath, hl); // If
464 }
465 }
466
467 // default reply OK for other methods
468 client->replyError(501, "Unsupported Method");
469 return false;
470 }
471
473 void setReference(void* ref) override { reference = ref; }
474
476 void* getReference() override { return reference; }
477
478 void logStatus() {
479 DlnaLogger.log(DlnaLogLevel::Info,
480 "Subscriptions: active=%d pending=%d / Scheduler: size=%d "
481 "active = %s",
482 subscription_mgr.subscriptionsCount(),
483 subscription_mgr.pendingCount(), scheduler.size(),
484 isSchedulerActive() ? "true" : "false");
485#ifdef ESP32
486 DlnaLogger.log(DlnaLogLevel::Info, "Memory: freeHeap=%u freePsram=%u",
487 (unsigned)ESP.getFreeHeap(), (unsigned)ESP.getFreePsram());
488#endif
489 }
490
491 protected:
492 bool is_active = false;
499 IUDPService* p_udp = nullptr;
506 void* reference = nullptr;
507
509 DlnaLogger.log(DlnaLogLevel::Debug, "setDeviceInfo");
510 p_device_info = &device;
512 }
513
516
518 uint32_t getSchedulerIntervalMs() const {
519 return (uint32_t)scheduler_interval_ms;
520 }
521
523 void setSubscriptionsIntervalMs(uint32_t ms) {
525 }
526
528 uint32_t getSubscriptionsIntervalMs() const {
529 return (uint32_t)subscriptions_interval_ms;
530 }
531
535 void logScheduledStatus(bool runImmediately = false) {
536 static uint64_t last_mem_log = 0;
537 const uint64_t MEM_LOG_INTERVAL_MS = 10000;
538 uint64_t now = millis();
539 if (runImmediately ||
540 (uint64_t)(now - last_mem_log) >= MEM_LOG_INTERVAL_MS) {
541 // update timestamp for next interval on all platforms
542 last_mem_log = now;
543 logStatus();
544 }
545 }
546
549 bool setupParser() {
550 DlnaLogger.log(DlnaLogLevel::Debug, "setupParser");
551 parser.addMSearchST("upnp:rootdevice");
552 parser.addMSearchST("ssdp:all");
555 // also register each service type, since some control points (e.g. VLC)
556 // search for services directly instead of the device type
557 for (auto& service : p_device_info->getServices()) {
558 parser.addMSearchST(service.service_type.c_str());
559 }
560 return true;
561 }
562
565 DlnaLogger.log(DlnaLogLevel::Debug, "setupScheduler");
566 // schedule post alive messages: Usually repeated 2 times (because UDP
567 // messages might be lost)
568 PostAliveSchedule* postAlive =
570 PostAliveSchedule* postAlive1 =
572 postAlive1->time = millis() + 100;
573 scheduler.add(postAlive);
574 scheduler.add(postAlive1);
575 return true;
576 }
577
580 DlnaLogger.log(DlnaLogLevel::Debug, "validateServiceInfo");
581 bool has_error = false;
582
583 if (!service.scpd_url || !*service.scpd_url) {
584 DlnaLogger.log(DlnaLogLevel::Error, "Service missing scpd_url");
585 has_error = true;
586 }
587 if (!service.control_url || !*service.control_url) {
588 DlnaLogger.log(DlnaLogLevel::Error, "Service missing control_url");
589 has_error = true;
590 }
591 if (!service.event_sub_url || !*service.event_sub_url) {
592 DlnaLogger.log(DlnaLogLevel::Error, "Service missing event_sub_url");
593 has_error = true;
594 }
595 if (!service.scp_cb) {
596 DlnaLogger.log(DlnaLogLevel::Error, "Service missing scp_cb for %s",
597 service.scpd_url ? service.scpd_url : "(null)");
598 has_error = true;
599 }
600 if (!service.control_cb) {
601 DlnaLogger.log(DlnaLogLevel::Error, "Service missing control_cb for %s",
602 service.control_url ? service.control_url : "(null)");
603 has_error = true;
604 }
605 if (!service.event_sub_cb) {
606 DlnaLogger.log(DlnaLogLevel::Error, "Service missing event_sub_cb for %s",
607 service.event_sub_url ? service.event_sub_url : "(null)");
608 has_error = true;
609 }
610
611 if (has_error) {
612 DlnaLogger.log(DlnaLogLevel::Error,
613 "Service validation failed - missing URLs/callbacks");
614 }
615
616 return !has_error;
617 }
618
620 virtual bool setupDLNAServer(IHttpServer& srv) {
621 DlnaLogger.log(DlnaLogLevel::Debug, "setupDLNAServer");
622 char buffer[DLNA_MAX_URL_LEN] = {0};
623 StrView url(buffer, DLNA_MAX_URL_LEN);
624
625 // add device url to server
626 const char* device_path = p_device_info->getDeviceURL().path();
627 const char* prefix = p_device_info->getBaseURL();
628
629 DlnaLogger.log(DlnaLogLevel::Info, "Setting up device path: %s",
630 device_path);
631 void* ref[] = {p_device_info};
632
633 if (!StrView(device_path).isEmpty()) {
634 p_server->rewrite("/", device_path);
635 p_server->rewrite("/dlna/device.xml", device_path);
636 p_server->rewrite("/device.xml", device_path);
637 p_server->rewrite("/index.html", device_path);
638 p_server->on(device_path, T_GET, deviceXMLCallback, ref, 1);
639 }
640
641 // Register icons and favicon.ico
642 for (Icon &icon : p_device_info->getIcons()) {
643 char tmp[DLNA_MAX_URL_LEN];
644 // const char* icon_path = url.buildPath(prefix, icon.icon_url);
645 p_server->on(icon.icon_url, T_GET, icon.mime,
646 (const uint8_t*)icon.icon_data, icon.icon_size);
647 if (icon.isfavicon) // provide favicon.ico
648 p_server->on("/favicon.ico", T_GET, icon.mime,
649 (const uint8_t*)icon.icon_data, icon.icon_size);
650 }
651
652 for (DLNAServiceInfo& service : p_device_info->getServices()) {
653 // Validate service before registering
654 if (!validateServiceInfo(service)) {
655 DlnaLogger.log(
657 "Skipping service registration due to validation failure");
658 continue;
659 }
660
661 p_server->on(service.scpd_url, T_GET, (web_callback_fn)service.scp_cb);
662 p_server->on(service.control_url, T_POST,
663 (web_callback_fn)service.control_cb);
664 p_server->on(service.event_sub_url, T_SUBSCRIBE,
665 (web_callback_fn)service.event_sub_cb);
666 p_server->on(service.event_sub_url, T_UNSUBSCRIBE,
667 (web_callback_fn)service.event_sub_cb);
668 p_server->on(service.event_sub_url, T_POST,
669 (web_callback_fn)service.event_sub_cb);
670 }
671
672 return true;
673 }
674
676 static void deviceXMLCallback(IClientHandler& client, IHttpServer* server,
677 const char* requestPath,
679 DlnaLogger.log(DlnaLogLevel::Debug, "deviceXMLCallback");
680
681 DLNADeviceInfo* device_xml = (DLNADeviceInfo*)(hl->context[0]);
682 assert(device_xml != nullptr);
683 if (device_xml != nullptr) {
684 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "DeviceXML");
685 // Use server->reply with a callback so the Content-Length is computed
686 // and headers are written correctly before streaming the body.
687 client.reply(
688 "text/xml",
689 [](Print& out, void* ref) -> size_t {
690 return ((DLNADeviceInfo*)ref)->print(out, ref);
691 },
692 200, "SUCCESS", device_xml);
693 } else {
694 DlnaLogger.log(DlnaLogLevel::Error, "DLNADevice is null");
695 client.replyNotFound();
696 }
697 }
698
700 static bool handleSubscribe(IClientHandler* client, IHttpServer* server,
701 const char* requestPath,
703 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscribe");
704 auto* device = static_cast<IDevice*>(server->getReference());
705 assert(device != nullptr);
706 DLNAServiceInfo* svc = device->getServiceByEventPath(requestPath);
707 assert(svc != nullptr);
708
709 // Delegate all HTTP processing to SubscriptionMgrDevice
710 return device->getSubscriptionMgr().processSubscribeRequest(*server, *svc,
711 client);
712 }
713
715 static bool handleUnsubscribe(IClientHandler* client, IHttpServer* server,
716 const char* requestPath,
718 DlnaLogger.log(DlnaLogLevel::Debug, "handleUnsubscribe");
719 auto* device = static_cast<IDevice*>(server->getReference());
720 assert(device != nullptr);
721 DLNAServiceInfo* svc = device->getServiceByEventPath(requestPath);
722 assert(svc != nullptr);
723
724 // Delegate all HTTP processing to SubscriptionMgrDevice
725 return device->getSubscriptionMgr().processUnsubscribeRequest(*server, *svc,
726 client);
727 }
728};
729
730} // namespace tiny_dlna
Represents a request to invoke a remote DLNA service action.
Definition: Action.h:104
void addArgument(Argument arg)
Definition: Action.h:113
void setAction(const char *act)
Definition: Action.h:155
const char * getAction()
Definition: Action.h:157
Device Attributes and generation of XML using urn:schemas-upnp-org:device-1-0. We could just return a...
Definition: DLNADeviceInfo.h:28
Vector< DLNAServiceInfo > & getServices()
Provides all service definitions.
Definition: DLNADeviceInfo.h:212
DLNAServiceInfo & getService(const char *id)
Finds a service definition by name.
Definition: DLNADeviceInfo.h:190
void setSubscriptionActive(bool flag)
Definition: DLNADeviceInfo.h:285
const char * getUDN()
Provide the udn uuid.
Definition: DLNADeviceInfo.h:92
const char * getBaseURL()
Provides the base url.
Definition: DLNADeviceInfo.h:122
Url & getDeviceURL()
This method returns base url/device.xml.
Definition: DLNADeviceInfo.h:133
virtual void setupServices(IHttpServer &server, IUDPService &udp)
to be implemented by subclasses
Definition: DLNADeviceInfo.h:317
DLNAServiceInfo & getServiceByAbbrev(const char *abbrev)
Finds a service definition by name.
Definition: DLNADeviceInfo.h:200
const char * getDeviceType()
Definition: DLNADeviceInfo.h:86
Vector< Icon > & getIcons()
Provides all icons.
Definition: DLNADeviceInfo.h:246
Translates DLNA UDP Requests to Schedule so that we can schedule a reply.
Definition: DLNADeviceRequestParser.h:15
Schedule * parse(DLNADeviceInfo &device, RequestData &req)
Definition: DLNADeviceRequestParser.h:20
void addMSearchST(const char *accept)
Definition: DLNADeviceRequestParser.h:18
Setup of a Basic DLNA Device service. The device registers itself to the network and answers to the D...
Definition: DLNADevice.h:47
void * reference
Definition: DLNADevice.h:506
void setSchedulerIntervalMs(uint32_t ms)
Set scheduler interval in milliseconds.
Definition: DLNADevice.h:515
uint64_t next_subscriptions_timeout_ms
Definition: DLNADevice.h:505
int loopUDPMessages()
Process incoming UDP and execute scheduled replies.
Definition: DLNADevice.h:192
void * getReference() override
Gets the reference pointer.
Definition: DLNADevice.h:476
static void deviceXMLCallback(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
callback to provide device XML
Definition: DLNADevice.h:676
bool is_active
Definition: DLNADevice.h:492
void setDeviceInfo(DLNADeviceInfo &device)
Definition: DLNADevice.h:508
uint64_t subscriptions_interval_ms
Definition: DLNADevice.h:502
void end() override
Stops the processing and releases the resources.
Definition: DLNADevice.h:119
void setReference(void *ref) override
Sets a reference pointer that can be used to associate application.
Definition: DLNADevice.h:473
void setSubscriptionsIntervalMs(uint32_t ms)
Set subscription publish interval in milliseconds.
Definition: DLNADevice.h:523
DLNAServiceInfo * getServiceByEventPath(const char *requestPath) override
Definition: DLNADevice.h:220
static size_t printReplyXML(Print &out, const char *replyName, const char *serviceId, std::function< size_t(Print &, void *)> valuesWriter=nullptr, void *ref=nullptr)
Builds a standard SOAP reply envelope.
Definition: DLNADevice.h:355
uint32_t getSchedulerIntervalMs() const
Get scheduler interval in milliseconds.
Definition: DLNADevice.h:518
static bool handleUnsubscribe(IClientHandler *client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Handle UNSUBSCRIBE requests.
Definition: DLNADevice.h:715
DLNAServiceInfo & getServiceByAbbrev(const char *abbrev) override
Get Service by subscription ns abbrev.
Definition: DLNADevice.h:213
static void parseActionRequest(IHttpServer *server, IClientHandler &client, const char *requestPath, HttpRequestHandlerLine *hl, ActionRequest &action)
Parses the SOAP content of a DLNA action request.
Definition: DLNADevice.h:277
bool setupScheduler()
Schedule PostAlive messages.
Definition: DLNADevice.h:564
uint64_t next_scheduler_timeout_ms
Definition: DLNADevice.h:504
IUDPService * p_udp
Definition: DLNADevice.h:499
void logScheduledStatus(bool runImmediately=false)
Definition: DLNADevice.h:535
bool begin(DLNADeviceInfo &device, IUDPService &udp, IHttpServer &server) override
start the
Definition: DLNADevice.h:54
int loopPublishSubscriptions()
Publish pending subscription notifications.
Definition: DLNADevice.h:180
static size_t replyGetCurrentConnectionInfo(Print &out, const char *protocolInfo, const char *connectionID, const char *direction)
Definition: DLNADevice.h:418
IHttpServer * p_server
Definition: DLNADevice.h:500
Scheduler scheduler
Definition: DLNADevice.h:495
uint32_t post_alive_repeat_ms
Definition: DLNADevice.h:494
DLNAServiceInfo & getService(const char *id) override
Provide addess to the service information.
Definition: DLNADevice.h:208
bool loop(int loopAction=RUN_ALL) override
Definition: DLNADevice.h:142
DLNADeviceInfo * p_device_info
Definition: DLNADevice.h:498
bool validateServiceInfo(const DLNAServiceInfo &service)
Validates that all required service URLs and callbacks are defined.
Definition: DLNADevice.h:579
void setPostAliveRepeatMs(uint32_t ms) override
Definition: DLNADevice.h:260
DLNADeviceRequestParser parser
Definition: DLNADevice.h:497
static size_t replyGetProtocolInfo(Print &out, const char *source="", const char *sink="")
Definition: DLNADevice.h:385
SubscriptionMgrDevice< ClientType > subscription_mgr
Definition: DLNADevice.h:496
void logStatus()
Definition: DLNADevice.h:478
virtual bool setupDLNAServer(IHttpServer &srv)
set up Web Server to handle Service Addresses
Definition: DLNADevice.h:620
static bool handleSubscription(IHttpServer *server, IClientHandler *client, const char *requestPath, HttpRequestHandlerLine *hl, bool &is_subscribe)
Static handler for SUBSCRIBE/UNSUBSCRIBE requests on service event URLs.
Definition: DLNADevice.h:448
uint32_t getSubscriptionsIntervalMs() const
Get subscription publish interval in milliseconds.
Definition: DLNADevice.h:528
bool setupParser()
Definition: DLNADevice.h:549
DLNADeviceInfo & getDeviceInfo() override
Provides the device.
Definition: DLNADevice.h:250
bool isSchedulerActive() override
Checks if the scheduler is active.
Definition: DLNADevice.h:256
bool loopServer() override
Process http server loop.
Definition: DLNADevice.h:183
ISubscriptionMgrDevice & getSubscriptionMgr() override
Get subscription manager for event handling.
Definition: DLNADevice.h:114
static size_t replyGetCurrentConnectionIDs(Print &out, const char *ids)
Definition: DLNADevice.h:402
void setSubscriptionsActive(bool flag) override
Enable or disable subscription notifications: call before begin.
Definition: DLNADevice.h:263
uint64_t scheduler_interval_ms
Definition: DLNADevice.h:501
bool isSubscriptionsActive() const override
Check if subscription notifications are active.
Definition: DLNADevice.h:272
void setSchedulerActive(bool flag) override
We can activate/deactivate the scheduler.
Definition: DLNADevice.h:253
static bool handleSubscribe(IClientHandler *client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Handle SUBSCRIBE requests.
Definition: DLNADevice.h:700
DLNADevice()
Definition: DLNADevice.h:49
bool is_subscriptions_active
Definition: DLNADevice.h:493
void addChange(const char *serviceAbbrev, std::function< size_t(Print &, void *)> changeWriter, void *ref) override
Record a state variable change for subscription notifications.
Definition: DLNADevice.h:232
Attributes needed for the DLNA Service Definition.
Definition: DLNAServiceInfo.h:18
http_callback control_cb
Definition: DLNAServiceInfo.h:44
http_callback scp_cb
Definition: DLNAServiceInfo.h:43
Str event_sub_url
Definition: DLNAServiceInfo.h:41
Str scpd_url
Definition: DLNAServiceInfo.h:39
http_callback event_sub_cb
Definition: DLNAServiceInfo.h:45
Str control_url
Definition: DLNAServiceInfo.h:40
TinyMethodID method()
Definition: HttpHeader.h:219
const char * get(const char *key)
Definition: HttpHeader.h:161
Used to register and process callbacks.
Definition: HttpRequestHandlerLine.h:12
void ** context
Definition: HttpRequestHandlerLine.h:37
Reading and writing of Http Requests.
Definition: HttpHeader.h:355
Definition: IHttpServer.h:19
virtual HttpRequestHeader & requestHeader()=0
virtual void replyNotFound()=0
virtual void reply(const char *contentType, Stream &inputStream, int size, int status=200, const char *msg=SUCCESS)=0
virtual void replyError(int err, const char *msg="Internal Server Error")=0
virtual Client * client()=0
Abstract interface for DLNA device functionality.
Definition: IDevice.h:30
Abstract interface for HTTP server functionality.
Definition: IHttpServer.h:50
virtual bool begin()=0
virtual void * getReference()=0
virtual bool doLoop()=0
Process server loop.
virtual void end()=0
virtual void on(const char *url, TinyMethodID method, web_callback_fn fn, void *ctx[]=nullptr, int ctxCount=0)=0
virtual void setReference(void *reference)=0
virtual void rewrite(const char *from, const char *to)=0
Abstract interface for UPnP event subscription management.
Definition: ISubscriptionMgrDevice.h:27
virtual void setSubscriptionsActive(bool flag)=0
Enable/disable subscription processing.
virtual void end()=0
Cleanup and stop subscription manager.
Abstract Interface for UDP API.
Definition: IUDPService.h:33
virtual RequestData receive()=0
Receive incoming UDP data and peer information.
virtual bool begin(int port)=0
Initialize UDP service on specified port.
Information about the icon.
Definition: Icon.h:10
Send out PostAlive messages: Repeated every 5 seconds.
Definition: Schedule.h:282
Send out ByeBye message.
Definition: Schedule.h:356
Scheduler which processes all due Schedules (to send out UDP replies)
Definition: Scheduler.h:15
int execute(IUDPService &udp)
Execute all due schedules.
Definition: Scheduler.h:30
void setActive(bool flag)
Definition: Scheduler.h:81
bool isActive()
Definition: Scheduler.h:83
int size()
Number of queued schedules.
Definition: Scheduler.h:79
void add(Schedule *schedule)
Add a schedule to the scheduler.
Definition: Scheduler.h:18
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 equals(const char *str)
checks if the string equals indicated parameter string
Definition: StrView.h:177
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: StrView.h:284
Heap-backed string utility used throughout tiny_dlna.
Definition: Str.h:27
bool endsWith(const char *suffix) const
True if ends with suffix (case-sensitive)
Definition: Str.h:175
const char * c_str() const
C-string pointer to internal buffer.
Definition: Str.h:88
Manages UPnP event subscriptions and queued notifications for a DLNA device.
Definition: SubscriptionMgrDevice.h:91
URL parser which breaks a full url string up into its individual parts.
Definition: Url.h:18
const char * path()
Definition: Url.h:40
Lightweight wrapper around std::vector with Arduino-friendly helpers and a pluggable allocator.
Definition: Vector.h:39
static void unEscape(Str &str)
Expands the predefined XML entities (< > " ' &)
Definition: XMLAttributeParser.h:203
Helper class that implements a Print interface to accumulate XML data and then parse it using XMLPars...
Definition: XMLParserPrint.h:16
size_t write(uint8_t ch) override
Writes a single byte to the buffer (Print interface)
Definition: XMLParserPrint.h:32
void end()
Resets the internal buffer.
Definition: XMLParserPrint.h:87
bool parse(Str &outNodeName, Vector< Str > &outPath, Str &outText, Str &outAttributes)
Parses the accumulated XML data and returns results via output parameters.
Definition: XMLParserPrint.h:59
#define DLNA_MAX_URL_LEN
app-wide max URL length
Definition: dlna_config.h:61
#define XML_PARSER_BUFFER_SIZE
Define XML parse buffer size.
Definition: dlna_config.h:41
#define DLNA_LOOP_DELAY_MS
Define delay in ms for main DLNA loop.
Definition: dlna_config.h:5
#define DLNA_RUN_SCHEDULER_EVERY_MS
Define scheduler run interval in ms.
Definition: dlna_config.h:10
#define DLNA_RUN_SUBSCRIPTIONS_EVERY_MS
Define subscription publish interval in ms.
Definition: dlna_config.h:15
Definition: Allocator.h:13
@ RUN_UDP
Definition: IDevice.h:18
@ RUN_SERVER
Definition: IDevice.h:18
@ RUN_ALL
Definition: IDevice.h:18
@ RUN_SUBSCRIPTIONS
Definition: IDevice.h:18
@ T_UNSUBSCRIBE
Definition: HttpHeader.h:46
@ T_SUBSCRIBE
Definition: HttpHeader.h:47
@ T_GET
Definition: HttpHeader.h:37
@ T_POST
Definition: HttpHeader.h:39
void(* web_callback_fn)(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *handlerLine)
Definition: IHttpServer.h:40
Provides information of the received UDP which consists of the (xml) data and the peer address and po...
Definition: IUDPService.h:22
An individual Schedule (to send out UDP messages)
Definition: Schedule.h:18
uint64_t time
Definition: Schedule.h:21
uint32_t repeat_ms
Definition: Schedule.h:23
Functions to efficiently output XML. XML data contains a lot of redundancy so it is more memory effic...
Definition: XMLPrinter.h:56
size_t printNodeBegin(const char *node, const char *attributes=nullptr, const char *ns=nullptr)
Prints the beginning of an XML node.
Definition: XMLPrinter.h:314
size_t printf(const char *fmt,...)
printf-style helper that formats into an internal buffer and writes to the configured Print output.
Definition: XMLPrinter.h:248
size_t printNodeEnd(const char *node, const char *ns=nullptr)
Prints the end of an XML node.
Definition: XMLPrinter.h:352