Skip to content

Commit

Permalink
Discovery mode revisited (#374)
Browse files Browse the repository at this point in the history
* Remove commented out code
* Replace discovery_mode with sneakily stashing Host header in the web::uri
  • Loading branch information
garethsb authored Feb 20, 2024
1 parent ba267fa commit 62a9165
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 78 deletions.
3 changes: 0 additions & 3 deletions Development/nmos-cpp-node/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,6 @@
// proxy_port [registry, node]: forward proxy port
//"proxy_port": 8080,

// discovery_mode [node]: whether the discovered host name (1) or resolved addresses (2) are used to construct request URLs for Registration APIs or System APIs
//"discovery_mode": 1,

// href_mode [registry, node]: whether the host name (1), addresses (2) or both (3) are used to construct response headers, and host and URL fields in the data model
//"href_mode": 1,

Expand Down
2 changes: 1 addition & 1 deletion Development/nmos/authorization_operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1200,7 +1200,7 @@ namespace nmos
const auto service = top_authorization_service(model.settings);

const auto auth_uri = service.second;
client.reset(new web::http::client::http_client(auth_uri, make_authorization_http_client_config(model.settings, load_ca_certificates, gate)));
client = nmos::details::make_http_client(auth_uri, make_authorization_http_client_config(model.settings, load_ca_certificates, gate));

auto token = cancellation_source.get_token();

Expand Down
2 changes: 1 addition & 1 deletion Development/nmos/authorization_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace nmos
, immediate(true)
, received(false) {}

pubkeys_shared_state(web::http::client::http_client client, nmos::api_version version, web::uri issuer)//, bool one_shot = false)
pubkeys_shared_state(web::http::client::http_client client, nmos::api_version version, web::uri issuer)
: engine(seeder)
, client(std::unique_ptr<web::http::client::http_client>(new web::http::client::http_client(client)))
, version(std::move(version))
Expand Down
20 changes: 20 additions & 0 deletions Development/nmos/client_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,26 @@ namespace nmos
return config;
}

namespace details
{
// make a client for the specified base_uri and config, with Host header sneakily stashed in user info
std::unique_ptr<web::http::client::http_client> make_http_client(const web::uri& base_uri, const web::http::client::http_client_config& client_config)
{
// unstash the Host header
// cf. nmos::details::resolve_service
const auto& host_name = base_uri.user_info();
std::unique_ptr<web::http::client::http_client> client(new web::http::client::http_client(web::uri_builder(base_uri).set_user_info({}).to_uri(), client_config));
if (!host_name.empty())
{
client->add_handler([host_name](web::http::http_request request, std::shared_ptr<web::http::http_pipeline_stage> next_stage) -> pplx::task<web::http::http_response>
{
request.headers().add(web::http::header_names::host, host_name);
return next_stage->propagate(request);
});
}
return client;
}
}

// make a request with logging
pplx::task<web::http::http_response> api_request(web::http::client::http_client client, web::http::http_request request, slog::base_gate& gate, const pplx::cancellation_token& token)
Expand Down
6 changes: 6 additions & 0 deletions Development/nmos/client_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ namespace nmos
web::websockets::client::websocket_client_config make_websocket_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, nmos::experimental::get_authorization_bearer_token_handler get_authorization_bearer_token, slog::base_gate& gate);
web::websockets::client::websocket_client_config make_websocket_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate);

namespace details
{
// make a client for the specified base_uri and config, with Host header sneakily stashed in user info
std::unique_ptr<web::http::client::http_client> make_http_client(const web::uri& base_uri_with_host_name_in_user_info, const web::http::client::http_client_config& client_config);
}

// make an API request with logging
pplx::task<web::http::http_response> api_request(web::http::client::http_client client, web::http::http_request request, slog::base_gate& gate, const pplx::cancellation_token& token = pplx::cancellation_token::none());
pplx::task<web::http::http_response> api_request(web::http::client::http_client client, const web::http::method& mtd, slog::base_gate& gate, const pplx::cancellation_token& token = pplx::cancellation_token::none());
Expand Down
84 changes: 22 additions & 62 deletions Development/nmos/mdns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,44 +495,11 @@ namespace nmos
update_service(advertiser, service, domain, settings, std::move(add_records));
}

enum discovery_mode
{
discovery_mode_default = 0,
discovery_mode_name = 1,
discovery_mode_addresses = 2
};

namespace details
{
typedef std::vector<resolved_service> resolved_services;

std::vector<utility::string_t> get_resolved_hosts(const mdns::resolve_result& resolved, const nmos::service_protocol& resolved_proto, discovery_mode mode)
{
std::vector<utility::string_t> results;

// by default, use the host name if secure communications are in use
if (mode == discovery_mode_name || (mode == discovery_mode_default && is_service_protocol_secure(resolved_proto)))
{
auto host_name = utility::s2us(resolved.host_name);
// remove a trailing '.' to turn an FQDN into a DNS name, for SSL certificate matching
// hmm, this might be more appropriately done by tweaking the Host header in the client request?
if (!host_name.empty() && U('.') == host_name.back()) host_name.pop_back();

results.push_back(host_name);
}

if (mode == discovery_mode_addresses || (mode == discovery_mode_default && !is_service_protocol_secure(resolved_proto)))
{
for (const auto& ip_address : resolved.ip_addresses)
{
results.push_back(utility::s2us(ip_address));
}
}

return results;
}

pplx::task<bool> resolve_service(std::shared_ptr<resolved_services> results, mdns::service_discovery& discovery, discovery_mode discovery_mode, 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, const std::chrono::steady_clock::time_point& timeout, const pplx::cancellation_token& token)
pplx::task<bool> resolve_service(std::shared_ptr<resolved_services> results, 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, const std::chrono::steady_clock::time_point& timeout, const pplx::cancellation_token& token)
{
return discovery.browse([=, &discovery](const mdns::browse_result& resolving)
{
Expand Down Expand Up @@ -588,12 +555,17 @@ namespace nmos
.set_path(U("/x-nmos/") + utility::s2us(details::service_api(service)));
}

auto resolved_hosts = get_resolved_hosts(resolved, resolved_proto, discovery_mode);
auto host_name = utility::s2us(resolved.host_name);
// remove a trailing '.' to turn an FQDN into a DNS name, for SSL certificate matching
if (!host_name.empty() && U('.') == host_name.back()) host_name.pop_back();

for (const auto& host : resolved_hosts)
for (const auto& ip_address : resolved.ip_addresses)
{
// sneakily stash the Host header in user info
// cf. nmos::details::make_http_client
results->push_back({ { *resolved_ver, resolved_pri }, resolved_uri
.set_host(host)
.set_user_info(host_name)
.set_host(utility::s2us(ip_address))
.to_uri()
});
}
Expand All @@ -618,7 +590,9 @@ namespace nmos
}
}

pplx::task<std::list<resolved_service>> resolve_service_(mdns::service_discovery& discovery, discovery_mode mode, 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)
// helper function for resolving instances of the specified service (API)
// with the highest version, highest priority instances at the front, and optionally services with the same priority ordered randomly
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)
{
const auto absolute_timeout = std::chrono::steady_clock::now() + timeout;

Expand Down Expand Up @@ -646,8 +620,8 @@ namespace nmos
};

const std::vector<pplx::task<bool>> both_tasks{
details::resolve_service(both_results[0], discovery, mode, nmos::service_types::register_, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, linked_token),
details::resolve_service(both_results[1], discovery, mode, service, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, linked_token)
details::resolve_service(both_results[0], discovery, nmos::service_types::register_, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, linked_token),
details::resolve_service(both_results[1], discovery, service, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, linked_token)
};

// when either task is completed, cancel and wait for the other to be completed
Expand Down Expand Up @@ -675,12 +649,12 @@ namespace nmos
}
else
{
resolve_task = details::resolve_service(results, discovery, mode, nmos::service_types::register_, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, token);
resolve_task = details::resolve_service(results, discovery, nmos::service_types::register_, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, token);
}
}
else
{
resolve_task = details::resolve_service(results, discovery, mode, service, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, token);
resolve_task = details::resolve_service(results, discovery, service, browse_domain, api_ver, priorities, api_proto, api_auth, absolute_timeout, token);
}

return resolve_task.then([results, randomize](bool)
Expand Down Expand Up @@ -722,9 +696,11 @@ namespace nmos
});
}

pplx::task<std::list<web::uri>> resolve_service(mdns::service_discovery& discovery, discovery_mode mode, 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)
// helper function for resolving instances of the specified service (API)
// with the highest version, highest priority instances at the front, and optionally services with the same priority ordered randomly
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)
{
return resolve_service_(discovery, mode, service, browse_domain, api_ver, priorities, api_proto, api_auth, randomize, timeout, token).then([](std::list<resolved_service> resolved_services)
return resolve_service_(discovery, service, browse_domain, api_ver, priorities, api_proto, api_auth, randomize, timeout, token).then([](std::list<resolved_service> resolved_services)
{
// add the version to each uri
return boost::copy_range<std::list<web::uri>>(resolved_services | boost::adaptors::transformed([](const resolved_service& s)
Expand All @@ -734,18 +710,10 @@ namespace nmos
});
}

// helper function for resolving instances of the specified service (API)
// with the highest version, highest priority instances at the front, and (by default) services with the same priority ordered randomly
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)
{
return resolve_service(discovery, discovery_mode_default, service, browse_domain, api_ver, priorities, api_proto, api_auth, randomize, timeout, token);
}

// helper function for resolving instances of the specified service (API) based on the specified settings
// with the highest version, highest priority instances at the front, and services with the same priority ordered randomly
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)
{
const auto mode = discovery_mode(nmos::experimental::fields::discovery_mode(settings));
const auto browse_domain = utility::us2s(nmos::get_domain(settings));
const auto versions = details::service_versions(service, settings);
const auto priorities = details::service_priorities(service, settings);
Expand All @@ -756,21 +724,13 @@ namespace nmos
// when no cancellation token is specified
const auto timeout = token.is_cancelable() ? nmos::fields::discovery_backoff_max(settings) : 1;

return resolve_service(discovery, mode, service, browse_domain, versions, priorities, protocols, authorization, true, std::chrono::seconds(timeout), token);
}

// helper function for resolving instances of the specified service (API)
// with the highest version, highest priority instances at the front, and (by default) services with the same priority ordered randomly
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)
{
return resolve_service_(discovery, discovery_mode_default, service, browse_domain, api_ver, priorities, api_proto, api_auth, randomize, timeout, token);
return resolve_service(discovery, service, browse_domain, versions, priorities, protocols, authorization, true, std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::seconds(timeout)), token);
}

// helper function for resolving instances of the specified service (API) based on the specified settings
// with the highest version, highest priority instances at the front, and services with the same priority ordered randomly
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)
{
const auto mode = discovery_mode(nmos::experimental::fields::discovery_mode(settings));
const auto browse_domain = utility::us2s(nmos::get_domain(settings));
const auto versions = details::service_versions(service, settings);
const auto priorities = details::service_priorities(service, settings);
Expand All @@ -781,7 +741,7 @@ namespace nmos
// when no cancellation token is specified
const auto timeout = token.is_cancelable() ? nmos::fields::discovery_backoff_max(settings) : 1;

return resolve_service_(discovery, mode, service, browse_domain, versions, priorities, protocols, authorization, true, std::chrono::seconds(timeout), token);
return resolve_service_(discovery, service, browse_domain, versions, priorities, protocols, authorization, true, std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::seconds(timeout)), token);
}
}
}
Loading

0 comments on commit 62a9165

Please sign in to comment.