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"
20#include "dlna/xml/XMLPrinter.h"
21#include "http/Http.h"
23
24namespace tiny_dlna {
25
45template <typename ClientType>
46class DLNADevice : public IDevice {
47 public:
49 // distribute initial subscription active flag to subscription manager
51 }
53 bool begin(DLNADeviceInfo& device, IUDPService& udp,
54 IHttpServer& server) override {
55 DlnaLogger.log(DlnaLogLevel::Info, "DLNADevice::begin");
56 server.setReference(this);
57 p_server = &server;
58 p_udp = &udp;
59 setDeviceInfo(device);
61
62 // check base url
63 const char* baseUrl = device.getBaseURL();
64 DlnaLogger.log(DlnaLogLevel::Info, "base URL: %s", baseUrl);
65
66 if (StrView(device.getBaseURL()).contains("localhost")) {
67 DlnaLogger.log(DlnaLogLevel::Error, "invalid base address: %s", baseUrl);
68 return false;
69 }
70
71 // setup device
72 device.setupServices(server, udp);
73
74 // ensure services reflect the current subscriptions-active flag
75 // (some callers rely on subscriptions being disabled by default)
77
78 // setup web server
79 if (!setupDLNAServer(server)) {
80 DlnaLogger.log(DlnaLogLevel::Error, "setupDLNAServer failed");
81 return false;
82 }
83
84 // start web server
85 Url url{baseUrl};
86 if (!p_server->begin()) {
87 DlnaLogger.log(DlnaLogLevel::Error, "Server failed");
88 return false;
89 }
90
91 // setup UDP
92 if (!p_udp->begin(DLNABroadcastAddress)) {
93 DlnaLogger.log(DlnaLogLevel::Error, "UDP begin failed");
94 return false;
95 }
96
97 // setup scheduler
98 if (!setupScheduler()) {
99 DlnaLogger.log(DlnaLogLevel::Error, "Scheduler failed");
100 return false;
101 }
102 // initialize scheduling timers so the scheduled tasks run immediately
103 uint64_t now = millis();
104 // set next timeouts to now so the first loop triggers the tasks
107
108 is_active = true;
109 DlnaLogger.log(DlnaLogLevel::Info, "Device successfully started");
110 return true;
111 }
112
114 return subscription_mgr;
115 }
116
118 void end() override {
119 DlnaLogger.log(DlnaLogLevel::Info, "DLNADevice::end");
120 p_server->end();
121
122 // send 3 bye messages
124 bye->repeat_ms = 800;
125 scheduler.add(bye);
126
127 // execute scheduler for 2 seconds
128 uint64_t end = millis() + 2000;
129 while (millis() < end) {
131 }
132
133 // end subscription manager
135
136 is_active = false;
137 }
138
141 bool loop(int loopAction = RUN_ALL) override {
142 if (!is_active) return false;
143 DlnaLogger.log(DlnaLogLevel::Debug, "loop");
144 // Platform-specific periodic diagnostics (e.g. ESP32 memory logging)
146 bool rc = false;
147
148 // handle server requests
149 if (loopAction & RUN_SERVER) {
150 rc = loopServer();
151 }
152
153 // Use millisecond-based intervals for scheduling so callers can set
154 // real-time intervals instead of loop-counts.
155 uint64_t now = millis();
156 if ((loopAction & RUN_UDP) && isSchedulerActive() &&
158 int count = loopUDPMessages();
159 if (count > 0) rc = true;
160 // schedule next run
162 }
163
164 // deliver any queued subscription notifications (if enabled)
165 if ((loopAction & RUN_SUBSCRIPTIONS) && isSubscriptionsActive() &&
167 int count = loopPublishSubscriptions();
168 if (count > 0) rc = true;
169 // schedule next run
171 }
172
173 // be nice, if we have other tasks
174 if (!rc) delay(DLNA_LOOP_DELAY_MS);
175 return true;
176 }
177
179 int loopPublishSubscriptions() { return subscription_mgr.publish(); }
180
182 bool loopServer() override {
183 DlnaLogger.log(DlnaLogLevel::Debug, "loopServer");
184 if (!is_active) return false;
185 bool rc = p_server->doLoop();
186 DlnaLogger.log(DlnaLogLevel::Debug, "server %s", rc ? "true" : "false");
187 return rc;
188 }
189
192 // process UDP requests
193 DlnaLogger.log(DlnaLogLevel::Debug, "loopUDPMessages");
194 RequestData req = p_udp->receive();
195 if (req) {
196 Schedule* schedule = parser.parse(*p_device_info, req);
197 if (schedule != nullptr) {
198 scheduler.add(schedule);
199 }
200 }
201
202 // execute scheduled udp replys
203 return scheduler.execute(*p_udp);
204 }
205
207 DLNAServiceInfo& getService(const char* id) override {
208 return p_device_info->getService(id);
209 }
210
212 DLNAServiceInfo& getServiceByAbbrev(const char* abbrev) override {
213 return p_device_info->getServiceByAbbrev(abbrev);
214 }
215
219 DLNAServiceInfo* getServiceByEventPath(const char* requestPath) override {
220 DlnaLogger.log(DlnaLogLevel::Debug, "getServiceByEventPath");
221 if (p_device_info == nullptr || requestPath == nullptr) return nullptr;
223 if (StrView(s.event_sub_url).equals(requestPath)) {
224 return &s;
225 }
226 }
227 return nullptr;
228 }
229
231 void addChange(const char* serviceAbbrev,
232 std::function<size_t(Print&, void*)> changeWriter,
233 void* ref) override {
234 DlnaLogger.log(DlnaLogLevel::Debug, "addChange");
235 auto& mgr = getSubscriptionMgr();
236 DLNAServiceInfo& serviceInfo = getServiceByAbbrev(serviceAbbrev);
237 if (!serviceInfo) {
238 DlnaLogger.log(DlnaLogLevel::Warning,
239 "addChange: No service info available for %s",
240 serviceAbbrev);
241 return;
242 }
243 mgr.addChange(serviceInfo, changeWriter, ref);
244 }
245
248
250 void setSchedulerActive(bool flag) override { scheduler.setActive(flag); }
251
253 bool isSchedulerActive() override { return scheduler.isActive(); }
254
257 void setPostAliveRepeatMs(uint32_t ms) override { post_alive_repeat_ms = ms; }
258
260 void setSubscriptionsActive(bool flag) override {
262 if (p_device_info != nullptr) {
264 }
266 }
267
269 bool isSubscriptionsActive() const override {
271 }
272
274 static void parseActionRequest(IHttpServer* server, IClientHandler& client,
275 const char* requestPath,
277 ActionRequest& action) {
278 DlnaLogger.log(DlnaLogLevel::Info, "parseActionRequest");
279 auto start = millis();
281 xp.setExpandEncoded(true);
282
283 Str outNodeName;
284 Vector<Str> outPath;
285 Str outText;
286 Str outAttributes;
287 bool is_attribute = false;
288 bool is_action = false;
289 Str actionName;
290 char buffer[XML_PARSER_BUFFER_SIZE];
291 // Client* client = server->client(); // No longer valid, use handler
292 // reference
293
294 auto* pClient = client.client();
295 const char* clen_str = client.requestHeader().get("Content-Length");
296 int remaining = -1;
297 if (clen_str != nullptr) {
298 char* endptr = nullptr;
299 long val = strtol(clen_str, &endptr, 10);
300 // only trust a header that actually parsed as a non-negative number;
301 // otherwise fall back to reading until the client stalls
302 if (endptr != clen_str && val >= 0) remaining = (int)val;
303 }
304 while (remaining != 0) {
305 size_t chunk = remaining < 0 || (size_t)remaining > sizeof(buffer)
306 ? sizeof(buffer)
307 : (size_t)remaining;
308 size_t len = pClient->readBytes(buffer, chunk);
309 if (len == 0) break;
310 if (remaining > 0) remaining -= (int)len;
311 xp.write((const uint8_t*)buffer, len);
312
313 while (xp.parse(outNodeName, outPath, outText, outAttributes)) {
314 if (is_attribute) {
315 const char* argName = outNodeName.c_str();
316 action.addArgument(argName, outText.c_str());
317 continue;
318 }
319 if (is_action) {
320 is_action = false;
321 is_attribute = true;
322 action.setAction(outNodeName.c_str());
323 DlnaLogger.log(DlnaLogLevel::Info, "action: %s", action.getAction());
324 continue;
325 }
326 // skip SOAP envelope wrappers (different clients use different
327 // namespace prefixes, e.g. s:Envelope, soap:Envelope, SOAP-ENV:Envelope)
328 if (outNodeName.endsWith("Envelope")) {
329 continue;
330 }
331 if (outNodeName.endsWith("Body")) {
332 is_action = true;
333 continue;
334 }
335 }
336 }
337 xp.end();
338 DlnaLogger.log(DlnaLogLevel::Info, "Parse took %d ms", millis() - start);
339 }
340
349 static size_t printReplyXML(
350 Print& out, const char* replyName, const char* serviceId,
351 std::function<size_t(Print&, void*)> valuesWriter = nullptr,
352 void* ref = nullptr) {
353 DlnaLogger.log(DlnaLogLevel::Debug, "printReplyXML");
354 XMLPrinter xp(out);
355 size_t result = 0;
356 result += xp.printNodeBegin(
357 "s:Envelope",
358 "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"");
359 result += xp.printNodeBegin("s:Body");
360 result +=
361 xp.printf("<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s:1\">\n",
362 replyName, serviceId);
363
364 // e.g.<u:return>Stop,Pause,Next,Seek</u:return> for
365 // QueryStateVariableResponse
366 if (valuesWriter) {
367 result += valuesWriter(out, ref);
368 }
369
370 result += xp.printf("</u:%s>\n", replyName);
371 result += xp.printNodeEnd("s:Body");
372 result += xp.printNodeEnd("s:Envelope");
373 return result;
374 }
379 static size_t replyGetProtocolInfo(Print& out, const char* source = "",
380 const char* sink = "") {
381 DlnaLogger.log(DlnaLogLevel::Debug, "replyGetProtocolInfo");
382 return printReplyXML(out, "GetProtocolInfoResponse", "ConnectionManager",
383 [source, sink](Print& o, void* ref) -> size_t {
384 (void)ref;
385 size_t written = 0;
386 written += o.print("<Source>");
387 written += o.print(StrView(source).c_str());
388 written += o.print("</Source>");
389 written += o.print("<Sink>");
390 written += o.print(StrView(sink).c_str());
391 written += o.print("</Sink>");
392 return written;
393 });
394 }
395
396 static size_t replyGetCurrentConnectionIDs(Print& out, const char* ids) {
397 DlnaLogger.log(DlnaLogLevel::Debug, "replyGetCurrentConnectionIDs");
398 return printReplyXML(out, "GetCurrentConnectionIDsResponse",
399 "ConnectionManager",
400 [ids](Print& o, void* ref) -> size_t {
401 (void)ref;
402 size_t written = 0;
403 // UPnP spec uses "CurrentConnectionIDs" as the
404 // response element name
405 written += o.print("<CurrentConnectionIDs>");
406 written += o.print(StrView(ids ? ids : "0").c_str());
407 written += o.print("</CurrentConnectionIDs>");
408 return written;
409 });
410 }
411
412 static size_t replyGetCurrentConnectionInfo(Print& out,
413 const char* protocolInfo,
414 const char* connectionID,
415 const char* direction) {
416 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscription");
417 return printReplyXML(
418 out, "GetCurrentConnectionInfoResponse", "ConnectionManager",
419 [protocolInfo, connectionID, direction](Print& o, void* ref) -> size_t {
420 (void)ref;
421 size_t written = 0;
422 // Write directly to out to avoid using a fixed-size intermediate
423 // buffer.
424 written += o.print("<RcsID>0</RcsID>");
425 written += o.print("<AVTransportID>0</AVTransportID>");
426 written += o.print("<ProtocolInfo>");
427 written += o.print(StrView(protocolInfo).c_str());
428 written += o.print("</ProtocolInfo>");
429 written += o.print("<PeerConnectionManager></PeerConnectionManager>");
430 written += o.print("<PeerConnectionID>");
431 written += o.print(StrView(connectionID).c_str());
432 written += o.print("</PeerConnectionID>");
433 written += o.print("<Direction>");
434 written += o.print(StrView(direction).c_str());
435 written += o.print("</Direction>");
436 written += o.print("<Status>OK</Status>");
437 return written;
438 });
439 }
440
442 static bool handleSubscription(IHttpServer* server, IClientHandler* client,
443 const char* requestPath,
445 bool& is_subscribe) {
446 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscription");
447 // logic separated and easier to test.
448 is_subscribe = false;
449 // Use client handler for request header and error reply
450 if (client) {
451 HttpRequestHeader& req = client->requestHeader();
452 if (req.method() == T_SUBSCRIBE) {
453 is_subscribe = true;
454 return handleSubscribe(client, server, requestPath, hl); // If needed
455 }
456 if (req.method() == T_UNSUBSCRIBE || req.method() == T_POST) {
457 return handleUnsubscribe(client, server, requestPath, hl); // If
458 }
459 }
460
461 // default reply OK for other methods
462 client->replyError(501, "Unsupported Method");
463 return false;
464 }
465
467 void setReference(void* ref) override { reference = ref; }
468
470 void* getReference() override { return reference; }
471
472 void logStatus() {
473 DlnaLogger.log(DlnaLogLevel::Info,
474 "Subscriptions: active=%d pending=%d / Scheduler: size=%d "
475 "active = %s",
476 subscription_mgr.subscriptionsCount(),
477 subscription_mgr.pendingCount(), scheduler.size(),
478 isSchedulerActive() ? "true" : "false");
479#ifdef ESP32
480 DlnaLogger.log(DlnaLogLevel::Info, "Memory: freeHeap=%u freePsram=%u",
481 (unsigned)ESP.getFreeHeap(), (unsigned)ESP.getFreePsram());
482#endif
483 }
484
485 protected:
486 bool is_active = false;
493 IUDPService* p_udp = nullptr;
500 void* reference = nullptr;
501
503 DlnaLogger.log(DlnaLogLevel::Debug, "setDeviceInfo");
504 p_device_info = &device;
506 }
507
510
512 uint32_t getSchedulerIntervalMs() const {
513 return (uint32_t)scheduler_interval_ms;
514 }
515
517 void setSubscriptionsIntervalMs(uint32_t ms) {
519 }
520
522 uint32_t getSubscriptionsIntervalMs() const {
523 return (uint32_t)subscriptions_interval_ms;
524 }
525
529 void logScheduledStatus(bool runImmediately = false) {
530 static uint64_t last_mem_log = 0;
531 const uint64_t MEM_LOG_INTERVAL_MS = 10000;
532 uint64_t now = millis();
533 if (runImmediately ||
534 (uint64_t)(now - last_mem_log) >= MEM_LOG_INTERVAL_MS) {
535 // update timestamp for next interval on all platforms
536 last_mem_log = now;
537 logStatus();
538 }
539 }
540
543 bool setupParser() {
544 DlnaLogger.log(DlnaLogLevel::Debug, "setupParser");
545 parser.addMSearchST("upnp:rootdevice");
546 parser.addMSearchST("ssdp:all");
549 // also register each service type, since some control points (e.g. VLC)
550 // search for services directly instead of the device type
551 for (auto& service : p_device_info->getServices()) {
552 parser.addMSearchST(service.service_type.c_str());
553 }
554 return true;
555 }
556
559 DlnaLogger.log(DlnaLogLevel::Debug, "setupScheduler");
560 // schedule post alive messages: Usually repeated 2 times (because UDP
561 // messages might be lost)
562 PostAliveSchedule* postAlive =
564 PostAliveSchedule* postAlive1 =
566 postAlive1->time = millis() + 100;
567 scheduler.add(postAlive);
568 scheduler.add(postAlive1);
569 return true;
570 }
571
574 DlnaLogger.log(DlnaLogLevel::Debug, "validateServiceInfo");
575 bool has_error = false;
576
577 if (!service.scpd_url || !*service.scpd_url) {
578 DlnaLogger.log(DlnaLogLevel::Error, "Service missing scpd_url");
579 has_error = true;
580 }
581 if (!service.control_url || !*service.control_url) {
582 DlnaLogger.log(DlnaLogLevel::Error, "Service missing control_url");
583 has_error = true;
584 }
585 if (!service.event_sub_url || !*service.event_sub_url) {
586 DlnaLogger.log(DlnaLogLevel::Error, "Service missing event_sub_url");
587 has_error = true;
588 }
589 if (!service.scp_cb) {
590 DlnaLogger.log(DlnaLogLevel::Error, "Service missing scp_cb for %s",
591 service.scpd_url ? service.scpd_url : "(null)");
592 has_error = true;
593 }
594 if (!service.control_cb) {
595 DlnaLogger.log(DlnaLogLevel::Error, "Service missing control_cb for %s",
596 service.control_url ? service.control_url : "(null)");
597 has_error = true;
598 }
599 if (!service.event_sub_cb) {
600 DlnaLogger.log(DlnaLogLevel::Error, "Service missing event_sub_cb for %s",
601 service.event_sub_url ? service.event_sub_url : "(null)");
602 has_error = true;
603 }
604
605 if (has_error) {
606 DlnaLogger.log(DlnaLogLevel::Error,
607 "Service validation failed - missing URLs/callbacks");
608 }
609
610 return !has_error;
611 }
612
614 virtual bool setupDLNAServer(IHttpServer& srv) {
615 DlnaLogger.log(DlnaLogLevel::Debug, "setupDLNAServer");
616 char buffer[DLNA_MAX_URL_LEN] = {0};
617 StrView url(buffer, DLNA_MAX_URL_LEN);
618
619 // add device url to server
620 const char* device_path = p_device_info->getDeviceURL().path();
621 const char* prefix = p_device_info->getBaseURL();
622
623 DlnaLogger.log(DlnaLogLevel::Info, "Setting up device path: %s",
624 device_path);
625 void* ref[] = {p_device_info};
626
627 if (!StrView(device_path).isEmpty()) {
628 p_server->rewrite("/", device_path);
629 p_server->rewrite("/dlna/device.xml", device_path);
630 p_server->rewrite("/device.xml", device_path);
631 p_server->rewrite("/index.html", device_path);
632 p_server->on(device_path, T_GET, deviceXMLCallback, ref, 1);
633 }
634
635 // Register icons and favicon.ico
636 for (Icon &icon : p_device_info->getIcons()) {
637 char tmp[DLNA_MAX_URL_LEN];
638 // const char* icon_path = url.buildPath(prefix, icon.icon_url);
639 p_server->on(icon.icon_url, T_GET, icon.mime,
640 (const uint8_t*)icon.icon_data, icon.icon_size);
641 if (icon.isfavicon) // provide favicon.ico
642 p_server->on("/favicon.ico", T_GET, icon.mime,
643 (const uint8_t*)icon.icon_data, icon.icon_size);
644 }
645
646 for (DLNAServiceInfo& service : p_device_info->getServices()) {
647 // Validate service before registering
648 if (!validateServiceInfo(service)) {
649 DlnaLogger.log(
651 "Skipping service registration due to validation failure");
652 continue;
653 }
654
655 p_server->on(service.scpd_url, T_GET, (web_callback_fn)service.scp_cb);
656 p_server->on(service.control_url, T_POST,
657 (web_callback_fn)service.control_cb);
658 p_server->on(service.event_sub_url, T_SUBSCRIBE,
659 (web_callback_fn)service.event_sub_cb);
660 p_server->on(service.event_sub_url, T_UNSUBSCRIBE,
661 (web_callback_fn)service.event_sub_cb);
662 p_server->on(service.event_sub_url, T_POST,
663 (web_callback_fn)service.event_sub_cb);
664 }
665
666 return true;
667 }
668
670 static void deviceXMLCallback(IClientHandler& client, IHttpServer* server,
671 const char* requestPath,
673 DlnaLogger.log(DlnaLogLevel::Debug, "deviceXMLCallback");
674
675 DLNADeviceInfo* device_xml = (DLNADeviceInfo*)(hl->context[0]);
676 assert(device_xml != nullptr);
677 if (device_xml != nullptr) {
678 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "DeviceXML");
679 // Use server->reply with a callback so the Content-Length is computed
680 // and headers are written correctly before streaming the body.
681 client.reply(
682 "text/xml",
683 [](Print& out, void* ref) -> size_t {
684 return ((DLNADeviceInfo*)ref)->print(out, ref);
685 },
686 200, "SUCCESS", device_xml);
687 } else {
688 DlnaLogger.log(DlnaLogLevel::Error, "DLNADevice is null");
689 client.replyNotFound();
690 }
691 }
692
694 static bool handleSubscribe(IClientHandler* client, IHttpServer* server,
695 const char* requestPath,
697 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscribe");
698 auto* device = static_cast<IDevice*>(server->getReference());
699 assert(device != nullptr);
700 DLNAServiceInfo* svc = device->getServiceByEventPath(requestPath);
701 assert(svc != nullptr);
702
703 // Delegate all HTTP processing to SubscriptionMgrDevice
704 return device->getSubscriptionMgr().processSubscribeRequest(*server, *svc,
705 client);
706 }
707
709 static bool handleUnsubscribe(IClientHandler* client, IHttpServer* server,
710 const char* requestPath,
712 DlnaLogger.log(DlnaLogLevel::Debug, "handleUnsubscribe");
713 auto* device = static_cast<IDevice*>(server->getReference());
714 assert(device != nullptr);
715 DLNAServiceInfo* svc = device->getServiceByEventPath(requestPath);
716 assert(svc != nullptr);
717
718 // Delegate all HTTP processing to SubscriptionMgrDevice
719 return device->getSubscriptionMgr().processUnsubscribeRequest(*server, *svc,
720 client);
721 }
722};
723
724} // 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:46
void * reference
Definition: DLNADevice.h:500
void setSchedulerIntervalMs(uint32_t ms)
Set scheduler interval in milliseconds.
Definition: DLNADevice.h:509
uint64_t next_subscriptions_timeout_ms
Definition: DLNADevice.h:499
int loopUDPMessages()
Process incoming UDP and execute scheduled replies.
Definition: DLNADevice.h:191
void * getReference() override
Gets the reference pointer.
Definition: DLNADevice.h:470
static void deviceXMLCallback(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
callback to provide device XML
Definition: DLNADevice.h:670
bool is_active
Definition: DLNADevice.h:486
void setDeviceInfo(DLNADeviceInfo &device)
Definition: DLNADevice.h:502
uint64_t subscriptions_interval_ms
Definition: DLNADevice.h:496
void end() override
Stops the processing and releases the resources.
Definition: DLNADevice.h:118
void setReference(void *ref) override
Sets a reference pointer that can be used to associate application.
Definition: DLNADevice.h:467
void setSubscriptionsIntervalMs(uint32_t ms)
Set subscription publish interval in milliseconds.
Definition: DLNADevice.h:517
DLNAServiceInfo * getServiceByEventPath(const char *requestPath) override
Definition: DLNADevice.h:219
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:349
uint32_t getSchedulerIntervalMs() const
Get scheduler interval in milliseconds.
Definition: DLNADevice.h:512
static bool handleUnsubscribe(IClientHandler *client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Handle UNSUBSCRIBE requests.
Definition: DLNADevice.h:709
DLNAServiceInfo & getServiceByAbbrev(const char *abbrev) override
Get Service by subscription ns abbrev.
Definition: DLNADevice.h:212
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:274
bool setupScheduler()
Schedule PostAlive messages.
Definition: DLNADevice.h:558
uint64_t next_scheduler_timeout_ms
Definition: DLNADevice.h:498
IUDPService * p_udp
Definition: DLNADevice.h:493
void logScheduledStatus(bool runImmediately=false)
Definition: DLNADevice.h:529
bool begin(DLNADeviceInfo &device, IUDPService &udp, IHttpServer &server) override
start the
Definition: DLNADevice.h:53
int loopPublishSubscriptions()
Publish pending subscription notifications.
Definition: DLNADevice.h:179
static size_t replyGetCurrentConnectionInfo(Print &out, const char *protocolInfo, const char *connectionID, const char *direction)
Definition: DLNADevice.h:412
IHttpServer * p_server
Definition: DLNADevice.h:494
Scheduler scheduler
Definition: DLNADevice.h:489
uint32_t post_alive_repeat_ms
Definition: DLNADevice.h:488
DLNAServiceInfo & getService(const char *id) override
Provide addess to the service information.
Definition: DLNADevice.h:207
bool loop(int loopAction=RUN_ALL) override
Definition: DLNADevice.h:141
DLNADeviceInfo * p_device_info
Definition: DLNADevice.h:492
bool validateServiceInfo(const DLNAServiceInfo &service)
Validates that all required service URLs and callbacks are defined.
Definition: DLNADevice.h:573
void setPostAliveRepeatMs(uint32_t ms) override
Definition: DLNADevice.h:257
DLNADeviceRequestParser parser
Definition: DLNADevice.h:491
static size_t replyGetProtocolInfo(Print &out, const char *source="", const char *sink="")
Definition: DLNADevice.h:379
SubscriptionMgrDevice< ClientType > subscription_mgr
Definition: DLNADevice.h:490
void logStatus()
Definition: DLNADevice.h:472
virtual bool setupDLNAServer(IHttpServer &srv)
set up Web Server to handle Service Addresses
Definition: DLNADevice.h:614
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:442
uint32_t getSubscriptionsIntervalMs() const
Get subscription publish interval in milliseconds.
Definition: DLNADevice.h:522
bool setupParser()
Definition: DLNADevice.h:543
DLNADeviceInfo & getDeviceInfo() override
Provides the device.
Definition: DLNADevice.h:247
bool isSchedulerActive() override
Checks if the scheduler is active.
Definition: DLNADevice.h:253
bool loopServer() override
Process http server loop.
Definition: DLNADevice.h:182
ISubscriptionMgrDevice & getSubscriptionMgr() override
Get subscription manager for event handling.
Definition: DLNADevice.h:113
static size_t replyGetCurrentConnectionIDs(Print &out, const char *ids)
Definition: DLNADevice.h:396
void setSubscriptionsActive(bool flag) override
Enable or disable subscription notifications: call before begin.
Definition: DLNADevice.h:260
uint64_t scheduler_interval_ms
Definition: DLNADevice.h:495
bool isSubscriptionsActive() const override
Check if subscription notifications are active.
Definition: DLNADevice.h:269
void setSchedulerActive(bool flag) override
We can activate/deactivate the scheduler.
Definition: DLNADevice.h:250
static bool handleSubscribe(IClientHandler *client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Handle SUBSCRIBE requests.
Definition: DLNADevice.h:694
DLNADevice()
Definition: DLNADevice.h:48
bool is_subscriptions_active
Definition: DLNADevice.h:487
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:231
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
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 setExpandEncoded(bool flag)
Forwards expand-entities setting to the underlying XMLParser.
Definition: XMLParserPrint.h:48
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