[wpinet] Handle empty txt block for mdns announcer (#4072)

This commit is contained in:
Thad House
2022-08-03 11:15:56 -07:00
committed by GitHub
parent 19ffebaf3e
commit 0a5eb65231
9 changed files with 129 additions and 25 deletions

View File

@@ -26,6 +26,18 @@ public class MulticastServiceAnnouncer implements AutoCloseable {
WPINetJNI.createMulticastServiceAnnouncer(serviceName, serviceType, port, keys, values);
}
/**
* Creates a MulticastServiceAnnouncer.
*
* @param serviceName service name
* @param serviceType service type
* @param port port
*/
public MulticastServiceAnnouncer(String serviceName, String serviceType, int port) {
m_handle =
WPINetJNI.createMulticastServiceAnnouncer(serviceName, serviceType, port, null, null);
}
@Override
public void close() {
WPINetJNI.freeMulticastServiceAnnouncer(m_handle);

View File

@@ -91,17 +91,21 @@ Java_edu_wpi_first_util_WPIUtilJNI_createMulticastServiceAnnouncer
JStringRef serviceNameRef{env, serviceName};
JStringRef serviceTypeRef{env, serviceType};
size_t keysLen = env->GetArrayLength(keys);
wpi::SmallVector<std::pair<std::string, std::string>, 8> txtVec;
txtVec.reserve(keysLen);
for (size_t i = 0; i < keysLen; i++) {
JLocal<jstring> key{
env, static_cast<jstring>(env->GetObjectArrayElement(keys, i))};
JLocal<jstring> value{
env, static_cast<jstring>(env->GetObjectArrayElement(values, i))};
txtVec.emplace_back(std::pair<std::string, std::string>{
JStringRef{env, key}.str(), JStringRef{env, value}.str()});
if (keys != nullptr && values != nullptr) {
size_t keysLen = env->GetArrayLength(keys);
txtVec.reserve(keysLen);
for (size_t i = 0; i < keysLen; i++) {
JLocal<jstring> key{
env, static_cast<jstring>(env->GetObjectArrayElement(keys, i))};
JLocal<jstring> value{
env, static_cast<jstring>(env->GetObjectArrayElement(values, i))};
txtVec.emplace_back(std::pair<std::string, std::string>{
JStringRef{env, key}.str(), JStringRef{env, value}.str()});
}
}
auto announcer = std::make_unique<wpi::MulticastServiceAnnouncer>(

View File

@@ -22,6 +22,8 @@ class MulticastServiceAnnouncer {
MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string_view, std::string_view>> txt);
MulticastServiceAnnouncer(std::string_view serviceName,
std::string_view serviceType, int port);
~MulticastServiceAnnouncer() noexcept;
void Start();
void Stop();

View File

@@ -58,6 +58,7 @@ AvahiFunctionTable::AvahiFunctionTable() {
AvahiFunctionLoad(service_resolver_free);
AvahiFunctionLoad(entry_group_new);
AvahiFunctionLoad(entry_group_free);
AvahiFunctionLoad(entry_group_add_service);
AvahiFunctionLoad(entry_group_add_service_strlst);
AvahiFunctionLoad(entry_group_reset);
AvahiFunctionLoad(entry_group_is_empty);

View File

@@ -263,6 +263,12 @@ class AvahiFunctionTable {
(AvahiClient*, AvahiEntryGroupCallback, void*));
AvahiFunction(entry_group_free, int, (AvahiEntryGroup*));
AvahiFunction(entry_group_add_service, int,
(AvahiEntryGroup * group, AvahiIfIndex interface,
AvahiProtocol protocol, AvahiPublishFlags flags,
const char* name, const char* type, const char* domain,
const char* host, uint16_t port, ...));
AvahiFunction(entry_group_add_service_strlst, int,
(AvahiEntryGroup * group, AvahiIfIndex interface,
AvahiProtocol protocol, AvahiPublishFlags flags,

View File

@@ -16,7 +16,7 @@ using namespace wpi;
struct MulticastServiceAnnouncer::Impl {
AvahiFunctionTable& table = AvahiFunctionTable::Get();
std::shared_ptr<AvahiThread> thread = AvahiThread::Get();
AvahiClient* client;
AvahiClient* client = nullptr;
AvahiEntryGroup* group = nullptr;
std::string serviceName;
std::string serviceType;
@@ -46,18 +46,22 @@ MulticastServiceAnnouncer::Impl::Impl(std::string_view serviceName,
this->serviceType = serviceType;
this->port = port;
std::vector<std::string> txts;
for (auto&& i : txt) {
txts.push_back(fmt::format("{}={}", i.first, i.second));
}
if (txt.empty()) {
this->stringList = nullptr;
} else {
std::vector<std::string> txts;
for (auto&& i : txt) {
txts.push_back(fmt::format("{}={}", i.first, i.second));
}
std::vector<const char*> txtArr;
for (auto&& i : txts) {
txtArr.push_back(i.c_str());
}
std::vector<const char*> txtArr;
for (auto&& i : txts) {
txtArr.push_back(i.c_str());
}
this->stringList =
this->table.string_list_new_from_array(txtArr.data(), txtArr.size());
this->stringList =
this->table.string_list_new_from_array(txtArr.data(), txtArr.size());
}
}
static void RegisterService(AvahiClient* client,
@@ -85,11 +89,19 @@ static void RegisterService(AvahiClient* client,
while (true) {
if (impl->table.entry_group_is_empty(impl->group)) {
auto ret = impl->table.entry_group_add_service_strlst(
impl->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
AVAHI_PUBLISH_USE_MULTICAST, impl->serviceName.c_str(),
impl->serviceType.c_str(), "local", nullptr, impl->port,
impl->stringList);
int ret = 0;
if (impl->stringList == nullptr) {
ret = impl->table.entry_group_add_service(
impl->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
AVAHI_PUBLISH_USE_MULTICAST, impl->serviceName.c_str(),
impl->serviceType.c_str(), "local", nullptr, impl->port, nullptr);
} else {
ret = impl->table.entry_group_add_service_strlst(
impl->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
AVAHI_PUBLISH_USE_MULTICAST, impl->serviceName.c_str(),
impl->serviceType.c_str(), "local", nullptr, impl->port,
impl->stringList);
}
if (ret == AVAHI_ERR_COLLISION) {
// Local collision
char* newName =
@@ -121,6 +133,12 @@ static void ClientCallback(AvahiClient* client, AvahiClientState state,
}
}
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port) {
wpi::span<const std::pair<std::string_view, std::string_view>> txt;
pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
}
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string, std::string>> txt) {

View File

@@ -22,6 +22,14 @@ struct MulticastServiceAnnouncer::Impl {
~Impl() noexcept { TXTRecordDeallocate(&txtRecord); }
};
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port) {
pImpl = std::make_unique<Impl>();
pImpl->serviceName = serviceName;
pImpl->serviceType = serviceType;
pImpl->port = port;
}
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string, std::string>> txt) {

View File

@@ -103,6 +103,12 @@ MulticastServiceAnnouncer::Impl::Impl(std::string_view serviceName,
std::wstring{wideStorage.data(), wideStorage.size()};
}
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port) {
wpi::span<const std::pair<std::string_view, std::string_view>> txt;
pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
}
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string, std::string>> txt) {

View File

@@ -0,0 +1,47 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <wpinet/MulticastServiceAnnouncer.h>
#include <wpinet/MulticastServiceResolver.h>
#include <thread>
#include <wpi/timestamp.h>
#include "gtest/gtest.h"
TEST(MulticastServiceAnnouncerTest, EmptyText) {
const std::string_view serviceName = "TestServiceNoText";
const std::string_view serviceType = "_wpinotxt";
const int port = std::rand();
wpi::MulticastServiceAnnouncer announcer(serviceName, serviceType, port);
wpi::MulticastServiceResolver resolver(serviceType);
if (announcer.HasImplementation() && resolver.HasImplementation()) {
announcer.Start();
resolver.Start();
std::this_thread::sleep_for(std::chrono::seconds(1));
resolver.Stop();
announcer.Stop();
}
}
TEST(MulticastServiceAnnouncerTest, SingleText) {
const std::string_view serviceName = "TestServiceSingle";
const std::string_view serviceType = "_wpitxt";
const int port = std::rand();
std::array<std::pair<std::string, std::string>, 1> txt = {
std::make_pair("hello", "world")};
wpi::MulticastServiceAnnouncer announcer(serviceName, serviceType, port, txt);
wpi::MulticastServiceResolver resolver(serviceType);
if (announcer.HasImplementation() && resolver.HasImplementation()) {
announcer.Start();
resolver.Start();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}