226 lines
8.4 KiB
JavaScript
226 lines
8.4 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.GetServiceInterface = exports.filledAddress = exports.addressHostToUrl = exports.getHostname = void 0;
|
|
exports.filterNonLocal = filterNonLocal;
|
|
exports.getOwnServiceInterface = getOwnServiceInterface;
|
|
exports.getServiceInterface = getServiceInterface;
|
|
const Host_1 = require("../interfaces/Host");
|
|
const ip_1 = require("./ip");
|
|
const deepEqual_1 = require("./deepEqual");
|
|
const once_1 = require("./once");
|
|
const Watchable_1 = require("./Watchable");
|
|
const getHostnameRegex = /^(\w+:\/\/)?([^\/\:]+)(:\d{1,3})?(\/)?/;
|
|
const getHostname = (url) => {
|
|
const founds = url.match(getHostnameRegex)?.[2];
|
|
if (!founds)
|
|
return null;
|
|
const parts = founds.split('@');
|
|
const last = parts[parts.length - 1];
|
|
return last;
|
|
};
|
|
exports.getHostname = getHostname;
|
|
const nonLocalFilter = {
|
|
exclude: {
|
|
kind: ['localhost', 'link-local', 'bridge'],
|
|
},
|
|
};
|
|
const publicFilter = {
|
|
visibility: 'public',
|
|
};
|
|
const either = (...args) => (a) => args.some((x) => x(a));
|
|
const negate = (fn) => (a) => !fn(a);
|
|
const unique = (values) => Array.from(new Set(values));
|
|
const addressHostToUrl = ({ scheme, sslScheme, username, suffix }, hostname) => {
|
|
const effectiveScheme = hostname.ssl ? sslScheme : scheme;
|
|
let host;
|
|
if (hostname.metadata.kind === 'ipv6') {
|
|
host = ip_1.IPV6_LINK_LOCAL.contains(hostname.hostname)
|
|
? `[${hostname.hostname}%${hostname.metadata.scopeId}]`
|
|
: `[${hostname.hostname}]`;
|
|
}
|
|
else {
|
|
host = hostname.hostname;
|
|
}
|
|
let portStr = '';
|
|
if (hostname.port !== null) {
|
|
const excludePort = effectiveScheme &&
|
|
effectiveScheme in Host_1.knownProtocols &&
|
|
hostname.port ===
|
|
Host_1.knownProtocols[effectiveScheme]
|
|
.defaultPort;
|
|
if (!excludePort)
|
|
portStr = `:${hostname.port}`;
|
|
}
|
|
return `${effectiveScheme ? `${effectiveScheme}://` : ''}${username ? `${username}@` : ''}${host}${portStr}${suffix}`;
|
|
};
|
|
exports.addressHostToUrl = addressHostToUrl;
|
|
function filterRec(hostnames, filter, invert) {
|
|
if (filter.predicate) {
|
|
const pred = filter.predicate;
|
|
hostnames = hostnames.filter((h) => invert !== pred(h));
|
|
}
|
|
if (filter.visibility === 'public')
|
|
hostnames = hostnames.filter((h) => invert !== h.public);
|
|
if (filter.visibility === 'private')
|
|
hostnames = hostnames.filter((h) => invert !== !h.public);
|
|
if (filter.kind) {
|
|
const kind = new Set(Array.isArray(filter.kind) ? filter.kind : [filter.kind]);
|
|
if (kind.has('ip')) {
|
|
kind.add('ipv4');
|
|
kind.add('ipv6');
|
|
}
|
|
hostnames = hostnames.filter((h) => invert !==
|
|
((kind.has('mdns') && h.metadata.kind === 'mdns') ||
|
|
(kind.has('domain') &&
|
|
(h.metadata.kind === 'private-domain' ||
|
|
h.metadata.kind === 'public-domain')) ||
|
|
(kind.has('ipv4') && h.metadata.kind === 'ipv4') ||
|
|
(kind.has('ipv6') && h.metadata.kind === 'ipv6') ||
|
|
(kind.has('localhost') &&
|
|
['localhost', '127.0.0.1', '::1'].includes(h.hostname)) ||
|
|
(kind.has('link-local') &&
|
|
h.metadata.kind === 'ipv6' &&
|
|
ip_1.IPV6_LINK_LOCAL.contains(ip_1.IpAddress.parse(h.hostname))) ||
|
|
(kind.has('bridge') &&
|
|
h.metadata.kind === 'ipv4' &&
|
|
h.metadata.gateway === 'lxcbr0') ||
|
|
(kind.has('plugin') && h.metadata.kind === 'plugin')));
|
|
}
|
|
if (filter.pluginId) {
|
|
const id = filter.pluginId;
|
|
hostnames = hostnames.filter((h) => invert !==
|
|
(h.metadata.kind === 'plugin' && h.metadata.packageId === id));
|
|
}
|
|
if (filter.exclude)
|
|
return filterRec(hostnames, filter.exclude, !invert);
|
|
return hostnames;
|
|
}
|
|
function isPublicIp(h) {
|
|
return h.public && (h.metadata.kind === 'ipv4' || h.metadata.kind === 'ipv6');
|
|
}
|
|
function enabledAddresses(addr) {
|
|
return addr.available.filter((h) => {
|
|
if (isPublicIp(h)) {
|
|
// Public IPs: disabled by default, explicitly enabled via SocketAddr string
|
|
if (h.port === null)
|
|
return true;
|
|
const sa = h.metadata.kind === 'ipv6'
|
|
? `[${h.hostname}]:${h.port}`
|
|
: `${h.hostname}:${h.port}`;
|
|
return addr.enabled.includes(sa);
|
|
}
|
|
else {
|
|
// Everything else: enabled by default, explicitly disabled via [hostname, port] tuple
|
|
return !addr.disabled.some(([hostname, port]) => hostname === h.hostname && port === (h.port ?? 0));
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Filters out localhost and IPv6 link-local hostnames from a list.
|
|
* Equivalent to the `nonLocal` filter on `Filled` addresses.
|
|
*/
|
|
function filterNonLocal(hostnames) {
|
|
return filterRec(hostnames, nonLocalFilter, false);
|
|
}
|
|
const filledAddress = (host, addressInfo) => {
|
|
const toUrl = exports.addressHostToUrl.bind(null, addressInfo);
|
|
const binding = host.bindings[addressInfo.internalPort];
|
|
const hostnames = binding ? enabledAddresses(binding.addresses) : [];
|
|
function filledAddressFromHostnames(hostnames) {
|
|
const getNonLocal = (0, once_1.once)(() => filledAddressFromHostnames(filterRec(hostnames, nonLocalFilter, false)));
|
|
const getPublic = (0, once_1.once)(() => filledAddressFromHostnames(filterRec(hostnames, publicFilter, false)));
|
|
return {
|
|
...addressInfo,
|
|
hostnames,
|
|
toUrl,
|
|
format: (format) => {
|
|
let res = hostnames;
|
|
if (format === 'hostname-info')
|
|
return res;
|
|
const urls = hostnames.map(toUrl);
|
|
if (format === 'url')
|
|
res = urls.map((u) => new URL(u));
|
|
else
|
|
res = urls;
|
|
return res;
|
|
},
|
|
filter: (filter) => {
|
|
return filledAddressFromHostnames(filterRec(hostnames, filter, false));
|
|
},
|
|
matchesAny: (filters) => {
|
|
const seen = new Set();
|
|
const union = [];
|
|
for (const f of filters) {
|
|
for (const h of filterRec(hostnames, f, false)) {
|
|
if (!seen.has(h)) {
|
|
seen.add(h);
|
|
union.push(h);
|
|
}
|
|
}
|
|
}
|
|
return filledAddressFromHostnames(union);
|
|
},
|
|
get nonLocal() {
|
|
return getNonLocal();
|
|
},
|
|
get public() {
|
|
return getPublic();
|
|
},
|
|
};
|
|
}
|
|
return filledAddressFromHostnames(hostnames);
|
|
};
|
|
exports.filledAddress = filledAddress;
|
|
const makeInterfaceFilled = async ({ effects, id, packageId, callback, }) => {
|
|
const serviceInterfaceValue = await effects.getServiceInterface({
|
|
serviceInterfaceId: id,
|
|
packageId,
|
|
callback,
|
|
});
|
|
if (!serviceInterfaceValue) {
|
|
return null;
|
|
}
|
|
const hostId = serviceInterfaceValue.addressInfo.hostId;
|
|
const host = await effects.getHostInfo({
|
|
packageId,
|
|
hostId,
|
|
callback,
|
|
});
|
|
const interfaceFilled = {
|
|
...serviceInterfaceValue,
|
|
host,
|
|
addressInfo: host
|
|
? (0, exports.filledAddress)(host, serviceInterfaceValue.addressInfo)
|
|
: null,
|
|
};
|
|
return interfaceFilled;
|
|
};
|
|
class GetServiceInterface extends Watchable_1.Watchable {
|
|
constructor(effects, opts, options) {
|
|
super(effects, options);
|
|
this.opts = opts;
|
|
this.label = 'GetServiceInterface';
|
|
}
|
|
fetch(callback) {
|
|
return makeInterfaceFilled({
|
|
effects: this.effects,
|
|
id: this.opts.id,
|
|
packageId: this.opts.packageId,
|
|
callback,
|
|
});
|
|
}
|
|
}
|
|
exports.GetServiceInterface = GetServiceInterface;
|
|
function getOwnServiceInterface(effects, id, map, eq) {
|
|
return new GetServiceInterface(effects, { id }, {
|
|
map: map ?? ((a) => a),
|
|
eq: eq ?? ((a, b) => (0, deepEqual_1.deepEqual)(a, b)),
|
|
});
|
|
}
|
|
function getServiceInterface(effects, opts, map, eq) {
|
|
return new GetServiceInterface(effects, opts, {
|
|
map: map ?? ((a) => a),
|
|
eq: eq ?? ((a, b) => (0, deepEqual_1.deepEqual)(a, b)),
|
|
});
|
|
}
|
|
//# sourceMappingURL=getServiceInterface.js.map
|