4#include "Stroika/Frameworks/StroikaPreComp.h"
10#include "Stroika/Foundation/IO/Network/ConnectionlessSocket.h"
13#include "Stroika/Foundation/Streams/MemoryStream.h"
18#include "SearchResponder.h"
25using namespace Stroika::Foundation::IO;
29using namespace Stroika::Frameworks;
30using namespace Stroika::Frameworks::UPnP;
31using namespace Stroika::Frameworks::UPnP::SSDP;
32using namespace Stroika::Frameworks::UPnP::SSDP::Server;
48#if USE_NOISY_TRACE_IN_THIS_MODULE_
52 static const String kNOTIFY_LEAD =
"M-SEARCH "sv;
54 SSDP::Advertisement da;
64 if (optional<size_t> n = line.
Find (
':')) {
68 if (not label.empty ()) {
69 da.fRawHeaders.Add (label, value);
71 constexpr auto kLabelComparer_ = String::ThreeWayComparer{Characters::eCaseInsensitive};
72 if (kLabelComparer_ (label,
"ST"sv) == 0) {
79 if (targetEqComparer (da.fTarget, kTarget_UPNPRootDevice)) {
82 else if (targetEqComparer (da.fTarget, kTarget_SSDPAll)) {
86 for (
const auto& a : advertisements) {
87 if (targetEqComparer (a.fTarget, da.fTarget)) {
95#if USE_NOISY_TRACE_IN_THIS_MODULE_
96 DbgTrace (L
"sending search responder advertisements...");
98 for (
auto a : advertisements) {
101 bool includeThisAdvertisement =
false;
102 if (targetEqComparer (da.fTarget, kTarget_SSDPAll)) {
103 includeThisAdvertisement =
true;
106 includeThisAdvertisement = targetEqComparer (a.fTarget, da.fTarget);
109 if (includeThisAdvertisement) {
110 Memory::BLOB data = SSDP::Serialize (
"HTTP/1.1 200 OK"sv, SearchOrNotify::SearchResponse, a);
111 useSocket.
SendTo (data, sendTo);
112#if USE_NOISY_TRACE_IN_THIS_MODULE_
113 DbgTrace (
"(location={},TARGET(ST/NT)={},USN={})"_f, a.fLocation, a.fTarget, a.fUSN);
125 advertisements.
Apply ([] ([[maybe_unused]]
const auto& a) { Require (not a.fTarget.empty ()); });
131 static constexpr Activity kActivity_{
"SSDP Binding in SearchResponder"sv};
133 constexpr unsigned int kMaxHops_ = 4;
136 s.
Bind (
SocketAddress{Network::V4::kAddrAny, UPnP::SSDP::V4::kSocketAddress.
GetPort ()}, Socket::BindFlags{.fSO_REUSEADDR =
true});
139 sockets += make_pair (s, UPnP::SSDP::V4::kSocketAddress);
143 s.
Bind (
SocketAddress{Network::V6::kAddrAny, UPnP::SSDP::V6::kSocketAddress.
GetPort ()}, Socket::BindFlags{.fSO_REUSEADDR =
true});
146 sockets += make_pair (s, UPnP::SSDP::V6::kSocketAddress);
151 static const String kThreadName_{
"SSDP Search Responder"sv};
153 [advertisements, sockets] () {
158 for (pair<ConnectionlessSocket::Ptr, SocketAddress> s : sockets) {
159 s.first.JoinMulticastGroup (s.second.GetInternetAddress ());
162 catch (
const system_error& e) {
166 if (e.code () == errc::no_such_device) {
168 DbgTrace (
"Got exception (errno: ENODEV) - while joining multicast group, so try again"_f);
185 size_t nBytesRead = s.
ReceiveFrom (buf, 0, &from).size ();
186 Assert (nBytesRead <= Memory::NEltsOf (buf));
187 using namespace Streams;
188 ParsePacketAndRespond_ (BinaryToText::Reader::New (ExternallyOwnedSpanInputStream::New<byte> (span{buf, nBytesRead})),
189 advertisements, s, from);
192 catch (
const Thread::AbortException&) {
202 Thread::eAutoStart, kThreadName_);
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual size_t length() const noexcept
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
nonvirtual String SubString(SZ from) const
nonvirtual String Trim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to Collection,...
nonvirtual void SendTo(span< const byte > data, const SocketAddress &sockAddr) const
nonvirtual span< byte > ReceiveFrom(span< byte > into, int flag, SocketAddress *fromAddress, Time::DurationSeconds timeout=Time::kInfinity) const
nonvirtual void SetMulticastLoopMode(bool loopMode) const
nonvirtual void SetMulticastTTL(uint8_t ttl) const
nonvirtual void Bind(const SocketAddress &sockAddr, BindFlags bindFlags=BindFlags{})
nonvirtual PortType GetPort() const
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
void Sleep(Time::Duration seconds2Wait)
ConnectionlessSocket::Ptr New(SocketAddress::FamilyType family, Type socketKind, const optional< IPPROTO > &protocol=nullopt)
bool SupportIPV4(IPVersionSupport flag)
bool SupportIPV6(IPVersionSupport flag)