Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Subscriber.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_Subscriber_h_
5#define _Stroika_Frameworks_UPnP_GENA_Subscriber_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <cstdint>
10#include <functional>
11#include <memory>
12
14#include "Stroika/Foundation/Containers/Mapping.h"
17#include "Stroika/Foundation/IO/Network/Transfer/Connection.h"
20
21#include "Stroika/Frameworks/WebServer/Message.h"
22
23/**
24 * \file
25 *
26 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
27 */
28
29namespace Stroika::Frameworks::UPnP::GENA {
30
31 using Foundation::Characters::String;
32 using Foundation::Containers::Mapping;
33 using Foundation::IO::Network::URI;
34
35 /**
36 * \brief A control point's subscription to a UPnP service's events (UPnP Device Architecture 1.1, section 4): Start SUBSCRIBEs at
37 * the service's eventSubURL, it renews before its TIMEOUT runs out, and Stop (or destruction) UNSUBSCRIBEs - and it reports
38 * each event the service sends it, each NOTIFY, which the control point's own web server routes to HandleNotify.
39 *
40 * The NOTIFYs come to the callback URL it gives the service, which must reach the control point's web server from the service:
41 * MakeCallbackURL makes one, with this machine's address on the network the service is on.
42 *
43 * Each event has its SEQ: 0 the first, with every evented variable, then 1, 2 ... in order - a SEQ missed is an event the
44 * service could not deliver. That first one can come before the SUBSCRIBE's answer, with the SID it is to be known by: so
45 * until that answer, a NOTIFY is taken whatever its SID - the callback URL being this subscription's own. And so route the
46 * NOTIFYs to it before Start: the first can come while Start is still waiting for that answer.
47 *
48 * If a renewal fails - the service forgot the subscription, say, restarting - it SUBSCRIBEs again, so has a new SID; and
49 * failing that, tries again a little later.
50 *
51 * \note THREADS: onEvent is called on the web server's thread that took the NOTIFY. Renewal is on IntervalTimer's thread, so
52 * it needs an IntervalTimer::Manager::Activator, as SSDP's BasicServer and CachingListener do.
53 *
54 * \par Example Usage
55 * \code
56 * // NOTIFYs to /light-events on port 8091 - routed to it by this web server - then subscribed
57 * GENA::Subscriber lightEvents{eventSubURL, GENA::Subscriber::MakeCallbackURL (eventSubURL, 8091, "/light-events"sv),
58 * [] (const GENA::Subscriber::Event& e) {
59 * // e.fVariables: what changed - say, {Status: 1}
60 * }};
61 * WebServer::ConnectionManager notifies{SocketAddresses (InternetAddresses_Any (), 8091),
62 * {Route{"NOTIFY"_RegEx, "light-events"_RegEx, [&] (Message& m) { lightEvents.HandleNotify (m); }}}};
63 * lightEvents.Start ();
64 * \endcode
65 */
66 class Subscriber {
67 public:
68 /**
69 */
70 struct Event {
71 uint32_t fSEQ;
72 /**
73 * The evented variables changed - in the first event, all of them - by name.
74 */
75 Mapping<String, String> fVariables;
76 };
77
78 public:
79 /**
80 * \note There is nothing here to validate a NOTIFY against, where a description has one (@see
81 * DeviceDescription::Options::fValidateReadAgainst) - @see GENA::Publisher::Options, which says why no schema
82 * for an event can exist.
83 */
84 struct Options {
85 /**
86 * The TIMEOUT asked of the service - which may grant less. It is renewed at half what it grants.
87 */
89
90 /**
91 * \brief How an event's XML is read - by default, with Stroika's default provider; nearly always left as is (@see
92 * XML::Options)
93 */
95
96 /**
97 * \brief How each SUBSCRIBE, renewal and UNSUBSCRIBE gets its connection, given the event URL it goes to - by default
98 * a new one, with the defaults
99 *
100 * For a timeout, or certificate checking, or connections leased from a Transfer::ConnectionPool (@see
101 * Publisher::Options::fConnectionFactory). Called on Start's and Stop's callers' threads, and on the renewals'.
102 */
105 };
106 static const Options kDefaultOptions;
107
108 public:
109 /**
110 * Not yet subscribed: Start does that.
111 */
112 Subscriber (const URI& eventSubURL, const URI& callbackURL, const function<void (const Event&)>& onEvent, const Options& options = kDefaultOptions);
113 Subscriber (const Subscriber&) = delete;
114 /**
115 * Stops - so unsubscribes.
116 */
118
119 public:
120 nonvirtual Subscriber& operator= (const Subscriber&) = delete;
121
122 public:
123 /**
124 * \brief Subscribe - throwing if the service does not take it - and keep it renewed. Started already: does nothing.
125 *
126 * Its events are read with the XML DOM: in a build without an XML parser (qStroika_Foundation_DataExchange_XML_SupportDOM),
127 * it throws.
128 */
129 nonvirtual void Start ();
130
131 public:
132 /**
133 * \brief Unsubscribe - failing quietly, as the service then drops it at its TIMEOUT - so no more events are reported.
134 * Not started: does nothing.
135 */
136 nonvirtual void Stop ();
137
138 public:
139 /**
140 * The ID the service knows the subscription by - new if it was subscribed again; nullopt when not subscribed.
141 */
142 nonvirtual optional<String> GetSID () const;
143
144 public:
145 /**
146 * \brief A NOTIFY at the callback URL - answered as UPnP Device Architecture 1.1, section 4.3, says: 200, or 400 Bad Request
147 * for no NT or NTS, 412 Precondition Failed for an NT or NTS not GENA's, a SID not this subscription's, or one
148 * when not started.
149 */
150 nonvirtual void HandleNotify (WebServer::Message& m);
151
152 public:
153 /**
154 * \brief A callback URL for the service at eventSubURL: http, at this machine's address on the network the service is on
155 * - the near end of a TCP connection to it - and port and path. So it connects to the service: throws if it cannot.
156 */
157 static URI MakeCallbackURL (const URI& eventSubURL, Foundation::IO::Network::PortType port, const String& path);
158
159 private:
160 class Rep_;
161 unique_ptr<Rep_> fRep_;
162 };
163
164 inline const Subscriber::Options Subscriber::kDefaultOptions;
165
166}
167
168#endif /*_Stroika_Frameworks_UPnP_GENA_Subscriber_h_*/
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
A control point's subscription to a UPnP service's events (UPnP Device Architecture 1....
Definition Subscriber.h:66
nonvirtual optional< String > GetSID() const
nonvirtual void Start()
Subscribe - throwing if the service does not take it - and keep it renewed. Started already: does not...
nonvirtual void Stop()
Unsubscribe - failing quietly, as the service then drops it at its TIMEOUT - so no more events are re...
nonvirtual void HandleNotify(WebServer::Message &m)
A NOTIFY at the callback URL - answered as UPnP Device Architecture 1.1, section 4....
static URI MakeCallbackURL(const URI &eventSubURL, Foundation::IO::Network::PortType port, const String &path)
A callback URL for the service at eventSubURL: http, at this machine's address on the network the ser...
How XML is read and written - for now, with which provider - taken last by each XML function (DOM::Do...
Foundation::Time::DurationSeconds fTimeout
Definition Subscriber.h:88
function< Foundation::IO::Network::Transfer::Connection::Ptr(const URI &to)> fConnectionFactory
How each SUBSCRIBE, renewal and UNSUBSCRIBE gets its connection, given the event URL it goes to - by ...
Definition Subscriber.h:103
Foundation::DataExchange::XML::Options fXMLOptions
How an event's XML is read - by default, with Stroika's default provider; nearly always left as is (.
Definition Subscriber.h:94