Skip to content

Commit 337a4b5

Browse files
simonbarenclaude
andcommitted
Address review feedback on TR-10-9 DNS-SD browse strategy
- Use string_enum for dns_sd_browse_mode (nmos::dns_sd_browse_modes::{both,unicast,mdns}) - Add dns_sd_browse_mode example + behaviour table to nmos-cpp-node config.json - Add expected-behaviour table to settings.h - Mark unused URI-returning resolve_service overload as deprecated (comment) - Refresh resolve_service / resolve_service_ doc comments in mdns.h/.cpp Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent cc1dab4 commit 337a4b5

6 files changed

Lines changed: 95 additions & 30 deletions

File tree

Development/cmake/NmosCppLibraries.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,7 @@ set(NMOS_CPP_NMOS_HEADERS
11331133
nmos/control_protocol_ws_api.h
11341134
nmos/device_type.h
11351135
nmos/did_sdid.h
1136+
nmos/dns_sd_browse_mode.h
11361137
nmos/event_type.h
11371138
nmos/events_api.h
11381139
nmos/events_resources.h

Development/nmos-cpp-node/config.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@
8585
// domain [registry, node]: the domain on which to browse for services or an empty string to use the default domain (specify "local." to explictly select mDNS)
8686
//"domain": "",
8787

88+
// dns_sd_browse_mode [node]: DNS-SD browse method per TR-10-9 Section 15
89+
// "both" (default) = unicast DNS first, mDNS fallback if unsuccessful
90+
// "unicast" = unicast DNS only
91+
// "mdns" = mDNS only
92+
// Expected resolve behaviour for each (mode, domain) combination:
93+
// mode | domain | behaviour
94+
// --------+-------------+--------------------
95+
// both | example.com | unicast -> mdns
96+
// both | local. | mdns
97+
// unicast | example.com | unicast
98+
// unicast | local. | mdns
99+
// mdns | example.com | mdns
100+
// mdns | local. | mdns
101+
//"dns_sd_browse_mode": "both",
102+
88103
// host_address/host_addresses [registry, node]: IP addresses used to construct response headers (e.g. 'Link' or 'Location'), and host and URL fields in the data model
89104
//"host_address": "127.0.0.1",
90105
//"host_addresses": array-of-ip-address-strings,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef NMOS_DNS_SD_BROWSE_MODE_H
2+
#define NMOS_DNS_SD_BROWSE_MODE_H
3+
4+
#include "nmos/string_enum.h"
5+
6+
namespace nmos
7+
{
8+
// DNS-SD browse method per TR-10-9 Section 15
9+
// Selected via the dns_sd_browse_mode setting in nmos::fields
10+
DEFINE_STRING_ENUM(dns_sd_browse_mode)
11+
namespace dns_sd_browse_modes
12+
{
13+
const dns_sd_browse_mode both{ U("both") }; // unicast DNS first, mDNS fallback if unsuccessful
14+
const dns_sd_browse_mode unicast{ U("unicast") }; // unicast DNS only
15+
const dns_sd_browse_mode mdns{ U("mdns") }; // mDNS only
16+
}
17+
}
18+
19+
#endif

Development/nmos/mdns.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "cpprest/uri_builder.h"
1313
#include "mdns/service_advertiser.h"
1414
#include "mdns/service_discovery.h"
15+
#include "nmos/dns_sd_browse_mode.h"
1516
#include "nmos/is09_versions.h"
1617
#include "nmos/is10_versions.h"
1718
#include "nmos/random.h"
@@ -600,8 +601,9 @@ namespace nmos
600601
}
601602
}
602603

603-
// helper function for resolving instances of the specified service (API)
604-
// with the highest version, highest priority instances at the front, and optionally services with the same priority ordered randomly
604+
// Helper function for resolving instances of the specified service (API), returning ((api_version, priority), uri)
605+
// tuples so callers can inspect the matched version and priority. Highest version and highest priority first,
606+
// and optionally services with the same priority ordered randomly.
605607
pplx::task<std::list<resolved_service>> resolve_service_(mdns::service_discovery& discovery, const nmos::service_type& service, const std::string& browse_domain, const std::set<nmos::api_version>& api_ver, const std::pair<nmos::service_priority, nmos::service_priority>& priorities, const std::set<nmos::service_protocol>& api_proto, const std::set<bool>& api_auth, bool randomize, const std::chrono::steady_clock::duration& timeout, const pplx::cancellation_token& token)
606608
{
607609
const auto absolute_timeout = std::chrono::steady_clock::now() + timeout;
@@ -706,8 +708,10 @@ namespace nmos
706708
});
707709
}
708710

709-
// helper function for resolving instances of the specified service (API)
710-
// with the highest version, highest priority instances at the front, and optionally services with the same priority ordered randomly
711+
// DEPRECATED: this overload is unused; prefer resolve_service_ which also returns api_ver/priority info.
712+
// Helper function for resolving instances of the specified service (API), returning a list of base URIs
713+
// (with the API version path appended), highest version and highest priority first, and optionally
714+
// services with the same priority ordered randomly.
711715
pplx::task<std::list<web::uri>> resolve_service(mdns::service_discovery& discovery, const nmos::service_type& service, const std::string& browse_domain, const std::set<nmos::api_version>& api_ver, const std::pair<nmos::service_priority, nmos::service_priority>& priorities, const std::set<nmos::service_protocol>& api_proto, const std::set<bool>& api_auth, bool randomize, const std::chrono::steady_clock::duration& timeout, const pplx::cancellation_token& token)
712716
{
713717
return resolve_service_(discovery, service, browse_domain, api_ver, priorities, api_proto, api_auth, randomize, timeout, token).then([](std::list<resolved_service> resolved_services)
@@ -720,12 +724,13 @@ namespace nmos
720724
});
721725
}
722726

723-
// helper function for resolving instances of the specified service (API) based on the specified settings
724-
// with the highest version, highest priority instances at the front, and services with the same priority ordered randomly
725-
// TR-10-9 Section 15: supports "both" (unicast DNS first, mDNS fallback), "unicast", and "mdns" browse modes
727+
// Helper function for resolving instances of the specified service (API) based on the specified settings,
728+
// returning a list of base URIs (with the API version path appended), highest version and highest priority
729+
// first, services with the same priority ordered randomly.
730+
// The browse method is selected by the dns_sd_browse_mode setting per TR-10-9 Section 15
731+
// (delegates to resolve_service_, which carries the dual-discovery logic).
726732
pplx::task<std::list<web::uri>> resolve_service(mdns::service_discovery& discovery, const nmos::service_type& service, const nmos::settings& settings, const pplx::cancellation_token& token)
727733
{
728-
// delegate to resolve_service_ (which has the dual-discovery logic) and transform results
729734
return resolve_service_(discovery, service, settings, token).then([](std::list<resolved_service> resolved_services)
730735
{
731736
return boost::copy_range<std::list<web::uri>>(resolved_services | boost::adaptors::transformed([](const resolved_service& s)
@@ -735,13 +740,17 @@ namespace nmos
735740
});
736741
}
737742

738-
// helper function for resolving instances of the specified service (API) based on the specified settings
739-
// with the highest version, highest priority instances at the front, and services with the same priority ordered randomly
740-
// TR-10-9 Section 15: supports "both" (unicast DNS first, mDNS fallback), "unicast", and "mdns" browse modes
743+
// Helper function for resolving instances of the specified service (API) based on the specified settings,
744+
// returning ((api_version, priority), uri) tuples. Highest version and highest priority first, services
745+
// with the same priority ordered randomly.
746+
// The browse method is selected by the dns_sd_browse_mode setting per TR-10-9 Section 15:
747+
// - "both" (default): unicast DNS first, mDNS fallback if unsuccessful
748+
// - "unicast" : unicast DNS only
749+
// - "mdns" : mDNS only
741750
pplx::task<std::list<resolved_service>> resolve_service_(mdns::service_discovery& discovery, const nmos::service_type& service, const nmos::settings& settings, const pplx::cancellation_token& token)
742751
{
743752
const auto browse_domain = utility::us2s(nmos::get_domain(settings));
744-
const auto browse_mode = utility::us2s(nmos::fields::dns_sd_browse_mode(settings));
753+
const auto browse_mode = nmos::dns_sd_browse_mode(nmos::fields::dns_sd_browse_mode(settings));
745754
const auto versions = details::service_versions(service, settings);
746755
const auto priorities = details::service_priorities(service, settings);
747756
const auto protocols = std::set<nmos::service_protocol>{ nmos::get_service_protocol(service, settings) };
@@ -753,8 +762,8 @@ namespace nmos
753762
const auto timeout_dur = std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::seconds(timeout));
754763

755764
// determine primary browse domain based on mode
756-
const auto primary_domain = (browse_mode == "mdns") ? std::string("local.") : browse_domain;
757-
const bool has_fallback = (browse_mode == "both") && !is_local_domain(browse_domain);
765+
const auto primary_domain = (browse_mode == nmos::dns_sd_browse_modes::mdns) ? std::string("local.") : browse_domain;
766+
const bool has_fallback = (browse_mode == nmos::dns_sd_browse_modes::both) && !is_local_domain(browse_domain);
758767

759768
// when there's a fallback, give the primary browse half the budget
760769
// so the mDNS fallback gets a meaningful allocation

Development/nmos/mdns.h

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,39 +157,50 @@ namespace nmos
157157
// helper function for updating the specified service (API) TXT records
158158
void update_service(mdns::service_advertiser& advertiser, const nmos::service_type& service, const nmos::settings& settings, mdns::structured_txt_records add_records = {});
159159

160-
// helper function for resolving instances of the specified service (API)
161-
// with the highest version, highest priority instances at the front, and (by default) services with the same priority ordered randomly
160+
// DEPRECATED: this overload is unused; prefer resolve_service_ which also returns api_ver/priority info.
161+
// Helper function for resolving instances of the specified service (API), returning a list of base URIs
162+
// (with the API version path appended), highest version and highest priority first, and (by default)
163+
// services with the same priority ordered randomly.
162164
pplx::task<std::list<web::uri>> resolve_service(mdns::service_discovery& discovery, const nmos::service_type& service, const std::string& browse_domain, const std::set<nmos::api_version>& api_ver, const std::pair<nmos::service_priority, nmos::service_priority>& priorities, const std::set<nmos::service_protocol>& api_proto, const std::set<bool>& api_auth, bool randomize, const std::chrono::steady_clock::duration& timeout, const pplx::cancellation_token& token = pplx::cancellation_token::none());
163165

164-
// helper function for resolving instances of the specified service (API) based on the specified options or defaults
165-
// with the highest version, highest priority instances at the front, and (by default) services with the same priority ordered randomly
166+
// DEPRECATED: convenience wrapper around the deprecated explicit-args resolve_service overload above.
167+
// Same behaviour, with default values for unspecified arguments.
166168
template <typename Rep = std::chrono::seconds::rep, typename Period = std::chrono::seconds::period>
167169
inline pplx::task<std::list<web::uri>> resolve_service(mdns::service_discovery& discovery, const nmos::service_type& service, const std::string& browse_domain = {}, const std::set<nmos::api_version>& api_ver = nmos::is04_versions::all, const std::pair<nmos::service_priority, nmos::service_priority>& priorities = { service_priorities::highest_active_priority, service_priorities::no_priority }, const std::set<nmos::service_protocol>& api_proto = nmos::service_protocols::all, const std::set<bool>& api_auth = { false, true }, bool randomize = true, const std::chrono::duration<Rep, Period>& timeout = std::chrono::seconds(mdns::default_timeout_seconds), const pplx::cancellation_token& token = pplx::cancellation_token::none())
168170
{
169171
return resolve_service(discovery, service, browse_domain, api_ver, api_proto, api_auth, randomize, std::chrono::duration_cast<std::chrono::steady_clock::duration>(timeout), token);
170172
}
171173

172-
// helper function for resolving instances of the specified service (API) based on the specified settings
173-
// with the highest version, highest priority instances at the front, and services with the same priority ordered randomly
174+
// Helper function for resolving instances of the specified service (API) based on the specified settings,
175+
// returning a list of base URIs (with the API version path appended), highest version and highest priority
176+
// first, services with the same priority ordered randomly.
177+
// The browse method is selected by the dns_sd_browse_mode setting per TR-10-9 Section 15
178+
// (delegates to resolve_service_, which carries the dual-discovery logic).
174179
pplx::task<std::list<web::uri>> resolve_service(mdns::service_discovery& discovery, const nmos::service_type& service, const nmos::settings& settings, const pplx::cancellation_token& token = pplx::cancellation_token::none());
175180

176181
typedef std::pair<api_version, service_priority> api_ver_pri;
177182
typedef std::pair<api_ver_pri, web::uri> resolved_service;
178183

179-
// helper function for resolving instances of the specified service (API)
180-
// with the highest version, highest priority instances at the front, and (by default) services with the same priority ordered randomly
184+
// Helper function for resolving instances of the specified service (API), returning ((api_version, priority), uri)
185+
// tuples so callers can inspect the matched version and priority. Highest version and highest priority first,
186+
// and (by default) services with the same priority ordered randomly.
181187
pplx::task<std::list<resolved_service>> resolve_service_(mdns::service_discovery& discovery, const nmos::service_type& service, const std::string& browse_domain, const std::set<nmos::api_version>& api_ver, const std::pair<nmos::service_priority, nmos::service_priority>& priorities, const std::set<nmos::service_protocol>& api_proto, const std::set<bool>& api_auth, bool randomize, const std::chrono::steady_clock::duration& timeout, const pplx::cancellation_token& token = pplx::cancellation_token::none());
182188

183-
// helper function for resolving instances of the specified service (API) based on the specified options or defaults
184-
// with the highest version, highest priority instances at the front, and (by default) services with the same priority ordered randomly
189+
// Convenience wrapper around the explicit-args resolve_service_ overload above, with default values
190+
// for unspecified arguments.
185191
template <typename Rep = std::chrono::seconds::rep, typename Period = std::chrono::seconds::period>
186192
inline pplx::task<std::list<resolved_service>> resolve_service_(mdns::service_discovery& discovery, const nmos::service_type& service, const std::string& browse_domain = {}, const std::set<nmos::api_version>& api_ver = nmos::is04_versions::all, const std::pair<nmos::service_priority, nmos::service_priority>& priorities = { service_priorities::highest_active_priority, service_priorities::no_priority }, const std::set<nmos::service_protocol>& api_proto = nmos::service_protocols::all, const std::set<bool>& api_auth = { false, true }, bool randomize = true, const std::chrono::duration<Rep, Period>& timeout = std::chrono::seconds(mdns::default_timeout_seconds), const pplx::cancellation_token& token = pplx::cancellation_token::none())
187193
{
188194
return resolve_service_(discovery, service, browse_domain, api_ver, api_proto, api_auth, randomize, std::chrono::duration_cast<std::chrono::steady_clock::duration>(timeout), token);
189195
}
190196

191-
// helper function for resolving instances of the specified service (API) based on the specified settings
192-
// with the highest version, highest priority instances at the front, and services with the same priority ordered randomly
197+
// Helper function for resolving instances of the specified service (API) based on the specified settings,
198+
// returning ((api_version, priority), uri) tuples. Highest version and highest priority first, services
199+
// with the same priority ordered randomly.
200+
// The browse method is selected by the dns_sd_browse_mode setting per TR-10-9 Section 15:
201+
// - "both" (default): unicast DNS first, mDNS fallback if unsuccessful
202+
// - "unicast" : unicast DNS only
203+
// - "mdns" : mDNS only
193204
pplx::task<std::list<resolved_service>> resolve_service_(mdns::service_discovery& discovery, const nmos::service_type& service, const nmos::settings& settings, const pplx::cancellation_token& token = pplx::cancellation_token::none());
194205
}
195206
}

Development/nmos/settings.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,20 @@ namespace nmos
8282
// domain [registry, node]: the domain on which to browse for services or an empty string to use the default domain (specify "local." to explictly select mDNS)
8383
const web::json::field_as_string_or domain{ U("domain"), U("") };
8484

85-
// dns_sd_browse_mode [node]: controls DNS-SD browse method per TR-10-9 Section 15
86-
// "both" (default) = unicast DNS first, mDNS fallback if unsuccessful
87-
// "unicast" = unicast DNS only (requires domain to be set to a non-local value)
88-
// "mdns" = mDNS only
85+
// dns_sd_browse_mode [node]: DNS-SD browse method per TR-10-9 Section 15
86+
// (see nmos::dns_sd_browse_modes for valid values)
87+
// "both" (default) = unicast DNS first, mDNS fallback if unsuccessful
88+
// "unicast" = unicast DNS only
89+
// "mdns" = mDNS only
90+
// Expected resolve behaviour for each (mode, domain) combination:
91+
// mode | domain | behaviour
92+
// --------+-------------+--------------------
93+
// both | example.com | unicast -> mdns
94+
// both | local. | mdns
95+
// unicast | example.com | unicast
96+
// unicast | local. | mdns
97+
// mdns | example.com | mdns
98+
// mdns | local. | mdns
8999
const web::json::field_as_string_or dns_sd_browse_mode{ U("dns_sd_browse_mode"), U("both") };
90100

91101
// host_address/host_addresses [registry, node]: IP addresses used to construct response headers (e.g. 'Link' or 'Location'), and host and URL fields in the data model

0 commit comments

Comments
 (0)