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 while (true) {
296 size_t len = pClient->readBytes(buffer, sizeof(buffer));
297 if (len == 0) break;
298 xp.write((const uint8_t*)buffer, len);
299
300 while (xp.parse(outNodeName, outPath, outText, outAttributes)) {
301 if (is_attribute) {
302 const char* argName = outNodeName.c_str();
303 action.addArgument(argName, outText.c_str());
304 continue;
305 }
306 if (is_action) {
307 is_action = false;
308 is_attribute = true;
309 action.setAction(outNodeName.c_str());
310 DlnaLogger.log(DlnaLogLevel::Info, "action: %s", action.getAction());
311 continue;
312 }
313 // skip SOAP envelope wrappers
314 if (outNodeName.equals("s:Envelope") || outNodeName.equals("Body")) {
315 continue;
316 }
317 if (outNodeName.equals("s:Body")) {
318 is_action = true;
319 }
320 }
321 }
322 xp.end();
323 DlnaLogger.log(DlnaLogLevel::Info, "Parse took %d ms", millis() - start);
324 }
325
334 static size_t printReplyXML(
335 Print& out, const char* replyName, const char* serviceId,
336 std::function<size_t(Print&, void*)> valuesWriter = nullptr,
337 void* ref = nullptr) {
338 DlnaLogger.log(DlnaLogLevel::Debug, "printReplyXML");
339 XMLPrinter xp(out);
340 size_t result = 0;
341 result += xp.printNodeBegin(
342 "s:Envelope",
343 "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n");
344 result += xp.printNodeBegin("s:Body");
345 result +=
346 xp.printf("<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s:1\">\n",
347 replyName, serviceId);
348
349 // e.g.<u:return>Stop,Pause,Next,Seek</u:return> for
350 // QueryStateVariableResponse
351 if (valuesWriter) {
352 result += valuesWriter(out, ref);
353 }
354
355 result += xp.printf("</u:%s>\n", replyName);
356 result += xp.printNodeEnd("s:Body");
357 result += xp.printNodeEnd("s:Envelope");
358 return result;
359 }
364 static size_t replyGetProtocolInfo(Print& out, const char* source = "",
365 const char* sink = "") {
366 DlnaLogger.log(DlnaLogLevel::Debug, "replyGetProtocolInfo");
367 return printReplyXML(out, "GetProtocolInfoResponse", "ConnectionManager",
368 [source, sink](Print& o, void* ref) -> size_t {
369 (void)ref;
370 size_t written = 0;
371 written += o.print("<Source>");
372 written += o.print(StrView(source).c_str());
373 written += o.print("</Source>");
374 written += o.print("<Sink>");
375 written += o.print(StrView(sink).c_str());
376 written += o.print("</Sink>");
377 return written;
378 });
379 }
380
381 static size_t replyGetCurrentConnectionIDs(Print& out, const char* ids) {
382 DlnaLogger.log(DlnaLogLevel::Debug, "replyGetCurrentConnectionIDs");
383 return printReplyXML(out, "GetCurrentConnectionIDsResponse",
384 "ConnectionManager",
385 [ids](Print& o, void* ref) -> size_t {
386 (void)ref;
387 size_t written = 0;
388 // UPnP spec uses "CurrentConnectionIDs" as the
389 // response element name
390 written += o.print("<CurrentConnectionIDs>");
391 written += o.print(StrView(ids ? ids : "0").c_str());
392 written += o.print("</CurrentConnectionIDs>");
393 return written;
394 });
395 }
396
397 static size_t replyGetCurrentConnectionInfo(Print& out,
398 const char* protocolInfo,
399 const char* connectionID,
400 const char* direction) {
401 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscription");
402 return printReplyXML(
403 out, "GetCurrentConnectionInfoResponse", "ConnectionManager",
404 [protocolInfo, connectionID, direction](Print& o, void* ref) -> size_t {
405 (void)ref;
406 size_t written = 0;
407 // Write directly to out to avoid using a fixed-size intermediate
408 // buffer.
409 written += o.print("<RcsID>0</RcsID>");
410 written += o.print("<AVTransportID>0</AVTransportID>");
411 written += o.print("<ProtocolInfo>");
412 written += o.print(StrView(protocolInfo).c_str());
413 written += o.print("</ProtocolInfo>");
414 written += o.print("<PeerConnectionManager></PeerConnectionManager>");
415 written += o.print("<PeerConnectionID>");
416 written += o.print(StrView(connectionID).c_str());
417 written += o.print("</PeerConnectionID>");
418 written += o.print("<Direction>");
419 written += o.print(StrView(direction).c_str());
420 written += o.print("</Direction>");
421 written += o.print("<Status>OK</Status>");
422 return written;
423 });
424 }
425
427 static bool handleSubscription(IHttpServer* server, IClientHandler* client,
428 const char* requestPath,
430 bool& is_subscribe) {
431 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscription");
432 // logic separated and easier to test.
433 is_subscribe = false;
434 // Use client handler for request header and error reply
435 if (client) {
436 HttpRequestHeader& req = client->requestHeader();
437 if (req.method() == T_SUBSCRIBE) {
438 is_subscribe = true;
439 return handleSubscribe(client, server, requestPath, hl); // If needed
440 }
441 if (req.method() == T_UNSUBSCRIBE || req.method() == T_POST) {
442 return handleUnsubscribe(client, server, requestPath, hl); // If
443 }
444 }
445
446 // default reply OK for other methods
447 client->replyError(501, "Unsupported Method");
448 return false;
449 }
450
452 void setReference(void* ref) override { reference = ref; }
453
455 void* getReference() override { return reference; }
456
457 void logStatus() {
458 DlnaLogger.log(DlnaLogLevel::Info,
459 "Subscriptions: active=%d pending=%d / Scheduler: size=%d "
460 "active = %s",
461 subscription_mgr.subscriptionsCount(),
462 subscription_mgr.pendingCount(), scheduler.size(),
463 isSchedulerActive() ? "true" : "false");
464#ifdef ESP32
465 DlnaLogger.log(DlnaLogLevel::Info, "Memory: freeHeap=%u freePsram=%u",
466 (unsigned)ESP.getFreeHeap(), (unsigned)ESP.getFreePsram());
467#endif
468 }
469
470 protected:
471 bool is_active = false;
478 IUDPService* p_udp = nullptr;
485 void* reference = nullptr;
486
488 DlnaLogger.log(DlnaLogLevel::Debug, "setDeviceInfo");
489 p_device_info = &device;
491 }
492
495
497 uint32_t getSchedulerIntervalMs() const {
498 return (uint32_t)scheduler_interval_ms;
499 }
500
502 void setSubscriptionsIntervalMs(uint32_t ms) {
504 }
505
507 uint32_t getSubscriptionsIntervalMs() const {
508 return (uint32_t)subscriptions_interval_ms;
509 }
510
514 void logScheduledStatus(bool runImmediately = false) {
515 static uint64_t last_mem_log = 0;
516 const uint64_t MEM_LOG_INTERVAL_MS = 10000;
517 uint64_t now = millis();
518 if (runImmediately ||
519 (uint64_t)(now - last_mem_log) >= MEM_LOG_INTERVAL_MS) {
520 // update timestamp for next interval on all platforms
521 last_mem_log = now;
522 logStatus();
523 }
524 }
525
528 bool setupParser() {
529 DlnaLogger.log(DlnaLogLevel::Debug, "setupParser");
530 parser.addMSearchST("upnp:rootdevice");
531 parser.addMSearchST("ssdp:all");
534 return true;
535 }
536
539 DlnaLogger.log(DlnaLogLevel::Debug, "setupScheduler");
540 // schedule post alive messages: Usually repeated 2 times (because UDP
541 // messages might be lost)
542 PostAliveSchedule* postAlive =
544 PostAliveSchedule* postAlive1 =
546 postAlive1->time = millis() + 100;
547 scheduler.add(postAlive);
548 scheduler.add(postAlive1);
549 return true;
550 }
551
554 DlnaLogger.log(DlnaLogLevel::Debug, "validateServiceInfo");
555 bool has_error = false;
556
557 if (!service.scpd_url || !*service.scpd_url) {
558 DlnaLogger.log(DlnaLogLevel::Error, "Service missing scpd_url");
559 has_error = true;
560 }
561 if (!service.control_url || !*service.control_url) {
562 DlnaLogger.log(DlnaLogLevel::Error, "Service missing control_url");
563 has_error = true;
564 }
565 if (!service.event_sub_url || !*service.event_sub_url) {
566 DlnaLogger.log(DlnaLogLevel::Error, "Service missing event_sub_url");
567 has_error = true;
568 }
569 if (!service.scp_cb) {
570 DlnaLogger.log(DlnaLogLevel::Error, "Service missing scp_cb for %s",
571 service.scpd_url ? service.scpd_url : "(null)");
572 has_error = true;
573 }
574 if (!service.control_cb) {
575 DlnaLogger.log(DlnaLogLevel::Error, "Service missing control_cb for %s",
576 service.control_url ? service.control_url : "(null)");
577 has_error = true;
578 }
579 if (!service.event_sub_cb) {
580 DlnaLogger.log(DlnaLogLevel::Error, "Service missing event_sub_cb for %s",
581 service.event_sub_url ? service.event_sub_url : "(null)");
582 has_error = true;
583 }
584
585 if (has_error) {
586 DlnaLogger.log(DlnaLogLevel::Error,
587 "Service validation failed - missing URLs/callbacks");
588 }
589
590 return !has_error;
591 }
592
594 virtual bool setupDLNAServer(IHttpServer& srv) {
595 DlnaLogger.log(DlnaLogLevel::Debug, "setupDLNAServer");
596 char buffer[DLNA_MAX_URL_LEN] = {0};
597 StrView url(buffer, DLNA_MAX_URL_LEN);
598
599 // add device url to server
600 const char* device_path = p_device_info->getDeviceURL().path();
601 const char* prefix = p_device_info->getBaseURL();
602
603 DlnaLogger.log(DlnaLogLevel::Info, "Setting up device path: %s",
604 device_path);
605 void* ref[] = {p_device_info};
606
607 if (!StrView(device_path).isEmpty()) {
608 p_server->rewrite("/", device_path);
609 p_server->rewrite("/dlna/device.xml", device_path);
610 p_server->rewrite("/device.xml", device_path);
611 p_server->rewrite("/index.html", device_path);
612 p_server->on(device_path, T_GET, deviceXMLCallback, ref, 1);
613 }
614
615 // Register icons and favicon.ico
616 for (Icon &icon : p_device_info->getIcons()) {
617 char tmp[DLNA_MAX_URL_LEN];
618 // const char* icon_path = url.buildPath(prefix, icon.icon_url);
619 p_server->on(icon.icon_url, T_GET, icon.mime,
620 (const uint8_t*)icon.icon_data, icon.icon_size);
621 if (icon.isfavicon) // provide favicon.ico
622 p_server->on("/favicon.ico", T_GET, icon.mime,
623 (const uint8_t*)icon.icon_data, icon.icon_size);
624 }
625
626 for (DLNAServiceInfo& service : p_device_info->getServices()) {
627 // Validate service before registering
628 if (!validateServiceInfo(service)) {
629 DlnaLogger.log(
631 "Skipping service registration due to validation failure");
632 continue;
633 }
634
635 p_server->on(service.scpd_url, T_GET, (web_callback_fn)service.scp_cb);
636 p_server->on(service.control_url, T_POST,
637 (web_callback_fn)service.control_cb);
638 p_server->on(service.event_sub_url, T_SUBSCRIBE,
639 (web_callback_fn)service.event_sub_cb);
640 p_server->on(service.event_sub_url, T_UNSUBSCRIBE,
641 (web_callback_fn)service.event_sub_cb);
642 p_server->on(service.event_sub_url, T_POST,
643 (web_callback_fn)service.event_sub_cb);
644 }
645
646 return true;
647 }
648
650 static void deviceXMLCallback(IClientHandler& client, IHttpServer* server,
651 const char* requestPath,
653 DlnaLogger.log(DlnaLogLevel::Debug, "deviceXMLCallback");
654
655 DLNADeviceInfo* device_xml = (DLNADeviceInfo*)(hl->context[0]);
656 assert(device_xml != nullptr);
657 if (device_xml != nullptr) {
658 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "DeviceXML");
659 // Use server->reply with a callback so the Content-Length is computed
660 // and headers are written correctly before streaming the body.
661 client.reply(
662 "text/xml",
663 [](Print& out, void* ref) -> size_t {
664 return ((DLNADeviceInfo*)ref)->print(out, ref);
665 },
666 200, "SUCCESS", device_xml);
667 } else {
668 DlnaLogger.log(DlnaLogLevel::Error, "DLNADevice is null");
669 client.replyNotFound();
670 }
671 }
672
674 static bool handleSubscribe(IClientHandler* client, IHttpServer* server,
675 const char* requestPath,
677 DlnaLogger.log(DlnaLogLevel::Debug, "handleSubscribe");
678 auto* device = static_cast<IDevice*>(server->getReference());
679 assert(device != nullptr);
680 DLNAServiceInfo* svc = device->getServiceByEventPath(requestPath);
681 assert(svc != nullptr);
682
683 // Delegate all HTTP processing to SubscriptionMgrDevice
684 return device->getSubscriptionMgr().processSubscribeRequest(*server, *svc,
685 client);
686 }
687
689 static bool handleUnsubscribe(IClientHandler* client, IHttpServer* server,
690 const char* requestPath,
692 DlnaLogger.log(DlnaLogLevel::Debug, "handleUnsubscribe");
693 auto* device = static_cast<IDevice*>(server->getReference());
694 assert(device != nullptr);
695 DLNAServiceInfo* svc = device->getServiceByEventPath(requestPath);
696 assert(svc != nullptr);
697
698 // Delegate all HTTP processing to SubscriptionMgrDevice
699 return device->getSubscriptionMgr().processUnsubscribeRequest(*server, *svc,
700 client);
701 }
702};
703
704} // 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:205
DLNAServiceInfo & getService(const char *id)
Finds a service definition by name.
Definition: DLNADeviceInfo.h:183
void setSubscriptionActive(bool flag)
Definition: DLNADeviceInfo.h:278
const char * getUDN()
Provide the udn uuid.
Definition: DLNADeviceInfo.h:90
const char * getBaseURL()
Provides the base url.
Definition: DLNADeviceInfo.h:120
Url & getDeviceURL()
This method returns base url/device.xml.
Definition: DLNADeviceInfo.h:131
virtual void setupServices(IHttpServer &server, IUDPService &udp)
to be implemented by subclasses
Definition: DLNADeviceInfo.h:307
DLNAServiceInfo & getServiceByAbbrev(const char *abbrev)
Finds a service definition by name.
Definition: DLNADeviceInfo.h:193
const char * getDeviceType()
Definition: DLNADeviceInfo.h:84
Vector< Icon > & getIcons()
Provides all icons.
Definition: DLNADeviceInfo.h:239
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:485
void setSchedulerIntervalMs(uint32_t ms)
Set scheduler interval in milliseconds.
Definition: DLNADevice.h:494
uint64_t next_subscriptions_timeout_ms
Definition: DLNADevice.h:484
int loopUDPMessages()
Process incoming UDP and execute scheduled replies.
Definition: DLNADevice.h:191
void * getReference() override
Gets the reference pointer.
Definition: DLNADevice.h:455
static void deviceXMLCallback(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
callback to provide device XML
Definition: DLNADevice.h:650
bool is_active
Definition: DLNADevice.h:471
void setDeviceInfo(DLNADeviceInfo &device)
Definition: DLNADevice.h:487
uint64_t subscriptions_interval_ms
Definition: DLNADevice.h:481
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:452
void setSubscriptionsIntervalMs(uint32_t ms)
Set subscription publish interval in milliseconds.
Definition: DLNADevice.h:502
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:334
uint32_t getSchedulerIntervalMs() const
Get scheduler interval in milliseconds.
Definition: DLNADevice.h:497
static bool handleUnsubscribe(IClientHandler *client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Handle UNSUBSCRIBE requests.
Definition: DLNADevice.h:689
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:538
uint64_t next_scheduler_timeout_ms
Definition: DLNADevice.h:483
IUDPService * p_udp
Definition: DLNADevice.h:478
void logScheduledStatus(bool runImmediately=false)
Definition: DLNADevice.h:514
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:397
IHttpServer * p_server
Definition: DLNADevice.h:479
Scheduler scheduler
Definition: DLNADevice.h:474
uint32_t post_alive_repeat_ms
Definition: DLNADevice.h:473
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:477
bool validateServiceInfo(const DLNAServiceInfo &service)
Validates that all required service URLs and callbacks are defined.
Definition: DLNADevice.h:553
void setPostAliveRepeatMs(uint32_t ms) override
Definition: DLNADevice.h:257
DLNADeviceRequestParser parser
Definition: DLNADevice.h:476
static size_t replyGetProtocolInfo(Print &out, const char *source="", const char *sink="")
Definition: DLNADevice.h:364
SubscriptionMgrDevice< ClientType > subscription_mgr
Definition: DLNADevice.h:475
void logStatus()
Definition: DLNADevice.h:457
virtual bool setupDLNAServer(IHttpServer &srv)
set up Web Server to handle Service Addresses
Definition: DLNADevice.h:594
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:427
uint32_t getSubscriptionsIntervalMs() const
Get subscription publish interval in milliseconds.
Definition: DLNADevice.h:507
bool setupParser()
Definition: DLNADevice.h:528
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:381
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:480
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:674
DLNADevice()
Definition: DLNADevice.h:48
bool is_subscriptions_active
Definition: DLNADevice.h:472
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
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:277
Send out ByeBye message.
Definition: Schedule.h:351
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 equals(const char *other) const
Exact string equality with C-string.
Definition: Str.h:167
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:55
#define XML_PARSER_BUFFER_SIZE
Define XML parse buffer size.
Definition: dlna_config.h:35
#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