Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
SearchResponder.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_UPnP_SSDP_Server_SearchResponder_h_
5#define _Stroika_Frameworks_UPnP_SSDP_Server_SearchResponder_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
10#include "Stroika/Foundation/Common/Common.h"
15
16#include "Stroika/Frameworks/UPnP/Device.h"
19#include "Stroika/Frameworks/UPnP/SSDP/Server/LocationProvider.h"
20
21/*
22 * \file
23 *
24 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
25 */
26
27namespace Stroika::Frameworks::UPnP::SSDP::Server {
28
29 using Traversal::Iterable;
30
31 /**
32 * Answers SSDP searches (M-SEARCH) that the given advertisements match, with where to find the device's description.
33 * Most applications want BasicServer instead, which does this and also announces the device (@see PeriodicNotifier).
34 *
35 * An advertisement for a device or service type (urn:domain:device:type:version, or service) answers a search for an older
36 * version of that type too, as that version - in its ST and its USN - as the UPnP Device Architecture requires, each version
37 * being compatible with those before it (1.1, sections 1.3.2 and 1.3.3). So advertise only the newest version.
38 *
39 * As the UPnP Device Architecture asks (1.1, section 1.3.3), each answer to a multicast M-SEARCH is sent after a random wait
40 * of up to its MX seconds (5 at most) - so devices, and a device's several answers, do not all come at once - while it goes
41 * on answering others; and a multicast M-SEARCH without an MX is ignored. A unicast one (its HOST not a multicast address)
42 * is answered at once - though on Windows none arrives while Windows' own SSDP Discovery service runs, as it binds port
43 * 1900 on each of the machine's addresses (@see the UPnP framework's ReadMe.md, SEARCHPORT.UPNP.ORG).
44 *
45 * Instantiating the class starts the (background) listening for searches automatically, and it continues
46 * until the SearchResponder object is destroyed. (so a smart pointer to one of these is typically kept around for the life of the application).
47 *
48 * \par Example Usage
49 * \code
50 * // answer searches for this device, as a UPnP root device - with where deviceWS serves its description
51 * SSDP::Advertisement rootDevice;
52 * rootDevice.fServer = SSDP::MakeServerHeaderValue ("MyProduct/1.0"sv);
53 * rootDevice.fTarget = SSDP::kTarget_UPNPRootDevice;
54 * rootDevice.fUSN = Characters::Format ("uuid:{}::{}"_f, deviceID, SSDP::kTarget_UPNPRootDevice);
55 * SearchResponder responder{Sequence<SSDP::Advertisement>{rootDevice}, LocationFromBindings (deviceWS.bindings ())};
56 * \endcode
57 *
58 * \note - this behavior differs from Stroika 2.1, where you had to explicitly call Run ()
59 *
60 * \note this uses its own thread, rather than using IntervalTimer, because it waits on input
61 * from the network, and must always be running/waiting
62 */
64 public:
65 /**
66 */
67 struct Options {
68 IO::Network::InternetProtocol::IP::IPVersionSupport fIPVersion{IO::Network::InternetProtocol::IP::IPVersionSupport::eDEFAULT}; ///< which SSDP channels: IPv4 (239.255.255.250) and/or IPv6 (ff02::c)
69 InterfaceFilter fInterfaces{DefaultInterfaceFilter}; ///< which network interfaces to listen for searches on (@see SSDP::InterfaceFilter)
70
71 /**
72 * When a network appears, listen for searches there too (it re-joins the multicast group on every interface).
73 * It costs a LinkMonitor (all share one thread, waiting on the OS's address-change notifications) - and, only while
74 * a burst of changes settles and is acted on, a thread of its own. Where the OS cannot tell (e.g. some containers),
75 * it is just not done.
76 */
77 bool fFollowNetworkChanges{true};
78 };
79 static const Options kDefaultOptions;
80
81 public:
82 /**
83 * Listens for searches on every network interface options.fInterfaces accepts (and, given fFollowNetworkChanges, on
84 * each that appears later), and answers
85 * each search the advertisements match with the LOCATION location gives for the asker: the context's fLocalAddress is
86 * this machine's address as the asker reaches it. The advertisements' own fLocation is not used.
87 */
88 SearchResponder (const Iterable<Advertisement>& advertisements, const LocationProvider& location, const Options& options = kDefaultOptions);
89 SearchResponder (const SearchResponder&) = delete;
90
91 public:
92 const SearchResponder operator= (const SearchResponder&) = delete;
93
94 public:
95 ~SearchResponder () = default;
96
97 public:
98 /**
99 * \brief The network interfaces it is listening for searches on now - as they were when it joined them (so with the
100 * addresses they had then). Safe to call from any thread.
101 */
103
104 private:
105 Execution::Synchronized<IO::Network::InterfacesByID> fListeningOn_; // set by the listening thread (so declared before it, outliving it)
106 unique_ptr<Execution::WaitForIOReady_Support::EventFD> fJoinAgain_{Execution::WaitForIOReady_Support::mkEventFD ()}; // set when a network appears, waking the listening thread to join it there too (so declared before it)
107 Execution::Thread::CleanupPtr fListenThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
108 optional<SSDP::Private_::NetworkChangeFollower> fNetworkChanges_; // last, so destroyed first: none follows while the rest goes away
109 };
110
111}
112
113/*
114 ********************************************************************************
115 ***************************** Implementation Details ***************************
116 ********************************************************************************
117 */
118#include "SearchResponder.inl"
119
120#endif /*_Stroika_Frameworks_UPnP_SSDP_Server_PeriodicNotifier_h_*/
bool DefaultInterfaceFilter(const Foundation::IO::Network::Interface &i)
SSDP's default InterfaceFilter: an interface that is running, and not loopback (whose multicast suppo...
function< bool(const Foundation::IO::Network::Interface &i)> InterfaceFilter
Which network interfaces SSDP is to talk on - asked of each interface every time SSDP lists them,...
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:234
nonvirtual IO::Network::InterfacesByID GetNetworkInterfaces() const
The network interfaces it is listening for searches on now - as they were when it joined them (so wit...