Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Publisher.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_UPnP_GENA_Publisher_h_
5#define _Stroika_Frameworks_UPnP_GENA_Publisher_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <functional>
10#include <memory>
11
13#include "Stroika/Foundation/Containers/Mapping.h"
14#include "Stroika/Foundation/IO/Network/Transfer/Connection.h"
17
18#include "Stroika/Frameworks/WebServer/Message.h"
19
20/**
21 * \file
22 *
23 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
24 */
25
26namespace Stroika::Frameworks::UPnP::GENA {
27
28 using Foundation::Characters::String;
29 using Foundation::Containers::Mapping;
30 using Foundation::IO::Network::URI;
31
32 /**
33 * \brief The eventing of one UPnP service (UPnP Device Architecture 1.1, section 4): control points SUBSCRIBE at the service's
34 * eventSubURL, and are sent a NOTIFY with each change of its evented state variables (those its description marks
35 * sendEvents="yes") - the first, as each subscribes, with all of them.
36 *
37 * The service's web server routes SUBSCRIBE and UNSUBSCRIBE at its eventSubURL to HandleRequest; the service calls Notify with
38 * each change. A subscription lasts its TIMEOUT - renewed by the subscriber, with a SUBSCRIBE giving its SID - or until it
39 * UNSUBSCRIBEs.
40 *
41 * Each subscription's events are numbered (SEQ): 0 its first, with every evented variable, then 1, 2 ... - wrapping to 1, never
42 * 0 again. That first one is sent once the SUBSCRIBE's answer has been, as the subscriber needs that answer's SID to know it -
43 * and with the variables' values as it is sent, so a change in between is in it.
44 *
45 * \note THREADS: HandleRequest and Notify may be called on any threads. The NOTIFYs are sent on a thread of the Publisher's own,
46 * one at a time, in the order they were made - each subscription's in SEQ order - to the first of the subscription's
47 * CALLBACK URLs that takes it. A subscriber that takes none stays subscribed (till its TIMEOUT); its next event still
48 * goes, numbered after the lost one, which is how a subscriber can tell an event was lost. currentState (the
49 * constructor's) is called on that thread too, holding no lock of the Publisher's.
50 *
51 * \par Example Usage
52 * \code
53 * atomic<bool> lightOn{false};
54 * GENA::Publisher events{[&] () { return Mapping<String, String>{{"Status"sv, lightOn ? "1"sv : "0"sv}}; }};
55 * // in its web server's routes:
56 * Route{"SUBSCRIBE|UNSUBSCRIBE"_RegEx, "SwitchPower/event"_RegEx, [&] (Message& m) { events.HandleRequest (m); }},
57 * ...
58 * lightOn = true;
59 * events.Notify ({{"Status"sv, "1"sv}});
60 * \endcode
61 */
62 class Publisher {
63 public:
64 /**
65 * \note There is nothing here to validate an event against, where a description has one (@see
66 * DeviceDescription::Options::fValidateReadAgainst). An event's elements are NAMED FOR THE STATE VARIABLES
67 * themselves, so no fixed schema could describe one. What UPnP publishes as event-1-0.xsd, beside the
68 * two real schemas, is a template for a device author to fill in - it declares
69 * <xsd:element name="[stateVariableName]">, twice, once for simple and once for complex content - and
70 * brackets are not legal in an XML name, so it compiles with nothing (libxml2, Xerces and .NET's
71 * XmlSchemaSet all refuse it, 2026-10-13). Nobody validates eventing: pupnp ships no schemas at all,
72 * and GUPnP renders exactly the two that are real.
73 */
74 struct Options {
75 /**
76 * The TIMEOUT a subscription gets when its SUBSCRIBE asks none - and the most one gets, asking more. The UPnP Device
77 * Architecture says SHOULD be at least 1800 seconds.
78 */
80
81 /**
82 * \brief How each NOTIFY gets its connection, given the callback URL it goes to - by default a new one, with the
83 * defaults
84 *
85 * For a timeout, or certificate checking, or connections leased from a Transfer::ConnectionPool - whose New takes the
86 * URL, so one already open to that subscriber is used again. Called on the publisher's own thread.
87 *
88 * \par Example Usage
89 * \code
90 * Transfer::ConnectionPool pool; // outliving the Publisher
91 * GENA::Publisher events{currentState, {.fConnectionFactory = [&pool] (const URI& to) { return pool.New (to); }}};
92 * \endcode
93 */
96 };
97 static const Options kDefaultOptions;
98
99 public:
100 /**
101 * currentState: the service's evented state variables - all of them, by name, with their values now - for each new
102 * subscription's first event.
103 */
104 Publisher (const function<Mapping<String, String> ()>& currentState, const Options& options = kDefaultOptions);
105 Publisher (const Publisher&) = delete;
106 ~Publisher ();
107
108 public:
109 nonvirtual Publisher& operator= (const Publisher&) = delete;
110
111 public:
112 /**
113 * \brief A SUBSCRIBE - a new subscription, or the renewal of one by its SID - or UNSUBSCRIBE, at the service's eventSubURL;
114 * answered as UPnP Device Architecture 1.1, section 4.1, says: 200 with the SID and TIMEOUT, 400 Bad Request for a
115 * SID given with an NT or a CALLBACK, 412 Precondition Failed for an NT other than upnp:event, no CALLBACK URL,
116 * or a SID it does not know.
117 */
118 nonvirtual void HandleRequest (WebServer::Message& m);
119
120 public:
121 /**
122 * \brief Send every subscriber these state variables' new values, by name.
123 */
124 nonvirtual void Notify (const Mapping<String, String>& changed);
125
126 private:
127 class Rep_;
128 unique_ptr<Rep_> fRep_;
129 };
130
131 inline const Publisher::Options Publisher::kDefaultOptions;
132
133}
134
135#endif /*_Stroika_Frameworks_UPnP_GENA_Publisher_h_*/
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
The eventing of one UPnP service (UPnP Device Architecture 1.1, section 4): control points SUBSCRIBE ...
Definition Publisher.h:62
nonvirtual void Notify(const Mapping< String, String > &changed)
Send every subscriber these state variables' new values, by name.
nonvirtual void HandleRequest(WebServer::Message &m)
A SUBSCRIBE - a new subscription, or the renewal of one by its SID - or UNSUBSCRIBE,...
function< Foundation::IO::Network::Transfer::Connection::Ptr(const URI &to)> fConnectionFactory
How each NOTIFY gets its connection, given the callback URL it goes to - by default a new one,...
Definition Publisher.h:94
Foundation::Time::DurationSeconds fTimeout
Definition Publisher.h:79