From d21ad324c2cc1d1ef47c1d8a1e01f714dc8d53ed Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Tue, 6 Apr 2021 22:00:13 +0300 Subject: [PATCH 1/2] feature: use vessel name as mdns serviceName https://tools.ietf.org/html/rfc6763#section-4.1.1 For the http service prefix the vessel name with SK. For the SK service types just use the vessel name. --- src/mdns.js | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/mdns.js b/src/mdns.js index ee7a6f100..f6ead43df 100644 --- a/src/mdns.js +++ b/src/mdns.js @@ -89,18 +89,15 @@ module.exports = function mdnsResponder(app) { txt: txtRecord } - const host = app.config.getExternalHostname() + const instanceName = getInstanceName(app.signalk) + const host = app.config.getExternalHostname() if (host !== require('os').hostname()) { options.host = host } - debug(options) - const ads = [] - // tslint:disable-next-line: forin - for (const i in types) { - const type = types[i] + types.forEach((type, i) => { debug( 'Starting mDNS ad: ' + type.type + @@ -109,14 +106,20 @@ module.exports = function mdnsResponder(app) { ':' + type.port ) - const ad = new mdns.Advertisement(type.type, type.port, options) + let name + if (instanceName) { + name = toUtfMaxLength(i === 0 ? `SK ${instanceName}` : instanceName) + } + const optionsForType = { name, ...options } + debug(optionsForType) + const ad = new mdns.Advertisement(type.type, type.port, optionsForType) ad.on('error', err => { console.log(type.type.name) console.error(err) }) ad.start() ads.push(ad) - } + }) return { stop: function() { @@ -127,3 +130,23 @@ module.exports = function mdnsResponder(app) { } } } + +const AD_NAME_MAX_UTF_LENGTH = 63 - 3 //allow prefix 'SK ' for http + +function getInstanceName(signalk) { + const full = signalk.retrieve() + return _.get(full, `${_.get(full, 'self')}.name`) +} + +function toUtfMaxLength(s) { + let result = s + while (utfLength(result) > AD_NAME_MAX_UTF_LENGTH) { + result = result.slice(0, result.length - 1) + } + return result +} + +function utfLength(s) { + // tslint:disable-next-line:no-bitwise + return ~-encodeURI(s).split(/%..|./).length +} From a456cd851c36194cb3bbe206b3068ea279f5a8d9 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Wed, 7 Apr 2021 21:43:57 +0300 Subject: [PATCH 2/2] feature: mdns retry with indexed names Add mdns advertisement retry with instance names that have the retry count appended to the name. Retries 9 times and gives up. --- src/mdns.js | 80 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/src/mdns.js b/src/mdns.js index f6ead43df..4edc26c1e 100644 --- a/src/mdns.js +++ b/src/mdns.js @@ -98,27 +98,7 @@ module.exports = function mdnsResponder(app) { const ads = [] types.forEach((type, i) => { - debug( - 'Starting mDNS ad: ' + - type.type + - ' ' + - app.config.getExternalHostname() + - ':' + - type.port - ) - let name - if (instanceName) { - name = toUtfMaxLength(i === 0 ? `SK ${instanceName}` : instanceName) - } - const optionsForType = { name, ...options } - debug(optionsForType) - const ad = new mdns.Advertisement(type.type, type.port, optionsForType) - ad.on('error', err => { - console.log(type.type.name) - console.error(err) - }) - ad.start() - ads.push(ad) + startAdWithNamedRetry(mdns, type, i, options, instanceName, ads, 0) }) return { @@ -131,6 +111,55 @@ module.exports = function mdnsResponder(app) { } } +const MAX_RETRIES = 9 + +function startAdWithNamedRetry( + mdns, + type, + i, + options, + instanceName, + ads, + retryIndex +) { + if (retryIndex > MAX_RETRIES) { + return + } + + debug(`Starting mDNS ad: ${type.type} ${type.host} ${type.port}`) + + let name + if (instanceName) { + name = toLengthCappedIndexedName( + i === 0 ? `SK ${instanceName}` : instanceName, + retryIndex + ) + } + const optionsForType = { name, ...options } + debug(optionsForType) + const ad = new mdns.Advertisement(type.type, type.port, optionsForType) + ad.on('error', err => { + console.log(type.type.name) + console.error(err) + try { + ad.stop() + } catch (e) { + console.error(e) + } + startAdWithNamedRetry( + mdns, + type, + i, + options, + instanceName, + ads, + retryIndex + 1 + ) + }) + ad.start() + ads[i] = ad +} + const AD_NAME_MAX_UTF_LENGTH = 63 - 3 //allow prefix 'SK ' for http function getInstanceName(signalk) { @@ -138,12 +167,15 @@ function getInstanceName(signalk) { return _.get(full, `${_.get(full, 'self')}.name`) } -function toUtfMaxLength(s) { +// return the string with utf length capped to 60 +// with retry count appended as -n for n >0 +function toLengthCappedIndexedName(s, retryIndex) { let result = s - while (utfLength(result) > AD_NAME_MAX_UTF_LENGTH) { + const maxLength = AD_NAME_MAX_UTF_LENGTH - (retryIndex > 0 ? '-X'.length : 0) + while (utfLength(result) > maxLength) { result = result.slice(0, result.length - 1) } - return result + return result + (retryIndex > 0 ? `-${retryIndex}` : '') } function utfLength(s) {