Feature/network (#2622)

* Feature: Add in the clear bindings

* wip: Working on network

* fix: Make it so the config gives the url

* chore: Remove the repeated types

* chore: Add in the todo's here

* chore: UPdate and remove some poorly name var

* chore: Remove the clear-bindings impl

* chore: Remove the wrapper

* handle HostnameInfo for Host bindings

Co-authored-by: Jade <Blu-J@users.noreply.github.com>

* ??

* chore: Make the install work

* Fix: Url's not being created

* chore: Fix the local onion in url

* include port in hostname

* Chore of adding a comment just to modify.

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
Co-authored-by: Jade <Blu-J@users.noreply.github.com>
This commit is contained in:
Jade
2024-06-06 15:39:54 -06:00
committed by GitHub
parent 412c5d68cc
commit 2c12af5af8
67 changed files with 798 additions and 1516 deletions

25
sdk/lib/util/Hostname.ts Normal file
View File

@@ -0,0 +1,25 @@
import { HostnameInfo } from "../types"
export function hostnameInfoToAddress(hostInfo: HostnameInfo): string {
if (hostInfo.kind === "onion") {
return `${hostInfo.hostname.value}`
}
if (hostInfo.kind !== "ip") {
throw Error("Expecting that the kind is ip.")
}
const hostname = hostInfo.hostname
if (hostname.kind === "domain") {
return `${hostname.subdomain ? `${hostname.subdomain}.` : ""}${hostname.domain}`
}
const port = hostname.sslPort || hostname.port
const portString = port ? `:${port}` : ""
if ("ipv4" === hostname.kind || "ipv6" === hostname.kind) {
return `${hostname.value}${portString}`
}
if ("local" === hostname.kind) {
return `${hostname.value}${portString}`
}
throw Error(
"Expecting to have a valid hostname kind." + JSON.stringify(hostname),
)
}

View File

@@ -1,10 +1,15 @@
import { ServiceInterfaceType } from "../StartSdk"
import { knownProtocols } from "../interfaces/Host"
import {
AddressInfo,
Effects,
HostInfo,
Host,
HostAddress,
Hostname,
HostnameInfo,
HostnameInfoIp,
HostnameInfoOnion,
IpInfo,
} from "../types"
export type UrlString = string
@@ -20,13 +25,13 @@ export const getHostname = (url: string): Hostname | null => {
}
export type Filled = {
hostnames: Hostname[]
onionHostnames: Hostname[]
localHostnames: Hostname[]
ipHostnames: Hostname[]
ipv4Hostnames: Hostname[]
ipv6Hostnames: Hostname[]
nonIpHostnames: Hostname[]
hostnames: HostnameInfo[]
onionHostnames: HostnameInfo[]
localHostnames: HostnameInfo[]
ipHostnames: HostnameInfo[]
ipv4Hostnames: HostnameInfo[]
ipv6Hostnames: HostnameInfo[]
nonIpHostnames: HostnameInfo[]
urls: UrlString[]
onionUrls: UrlString[]
@@ -50,7 +55,7 @@ export type ServiceInterfaceFilled = {
/** Whether or not to mask the URIs for this interface. Useful if the URIs contain sensitive information, such as a password, macaroon, or API key */
masked: boolean
/** Information about the host for this binding */
hostInfo: HostInfo
host: Host
/** URI information */
addressInfo: FilledAddressInfo
/** Indicates if we are a ui/p2p/api for the kind of interface that this is representing */
@@ -69,110 +74,103 @@ const negate =
(a: A) =>
!fn(a)
const unique = <A>(values: A[]) => Array.from(new Set(values))
function stringifyHostname(info: HostnameInfo): Hostname {
let base: string
if ("kind" in info.hostname && info.hostname.kind === "domain") {
base = info.hostname.subdomain
? `${info.hostname.subdomain}.${info.hostname.domain}`
: info.hostname.domain
} else {
base = info.hostname.value
export const addressHostToUrl = (
{ scheme, sslScheme, username, suffix }: AddressInfo,
host: HostnameInfo,
): UrlString[] => {
const res = []
const fmt = (scheme: string | null, host: HostnameInfo, port: number) => {
const includePort =
scheme &&
scheme in knownProtocols &&
port === knownProtocols[scheme as keyof typeof knownProtocols].defaultPort
let hostname
if (host.kind === "onion") {
hostname = host.hostname.value
} else if (host.kind === "ip") {
if (host.hostname.kind === "domain") {
hostname = `${host.hostname.subdomain ? `${host.hostname.subdomain}.` : ""}${host.hostname.domain}`
} else {
hostname = host.hostname.value
}
}
return `${scheme ? `${scheme}://` : ""}${
username ? `${username}@` : ""
}${hostname}${includePort ? `:${port}` : ""}${suffix}`
}
if (info.hostname.port && info.hostname.sslPort) {
return `${base}:${info.hostname.port}` as Hostname
} else if (info.hostname.sslPort) {
return `${base}:${info.hostname.sslPort}` as Hostname
} else if (info.hostname.port) {
return `${base}:${info.hostname.port}` as Hostname
if (host.hostname.sslPort !== null) {
res.push(fmt(sslScheme, host, host.hostname.sslPort))
}
return base as Hostname
}
const addressHostToUrl = (
{ bindOptions, username, suffix }: AddressInfo,
host: Hostname,
): UrlString => {
const scheme = host.endsWith(".onion")
? bindOptions.scheme
: bindOptions.addSsl
? bindOptions.addSsl.scheme
: bindOptions.scheme // TODO: encode whether hostname transport is "secure"?
return `${scheme ? `${scheme}//` : ""}${
username ? `${username}@` : ""
}${host}${suffix}`
if (host.hostname.port !== null) {
res.push(fmt(scheme, host, host.hostname.port))
}
return res
}
export const filledAddress = (
hostInfo: HostInfo,
host: Host,
addressInfo: AddressInfo,
): FilledAddressInfo => {
const toUrl = addressHostToUrl.bind(null, addressInfo)
const hostnameInfo =
hostInfo.kind == "multi"
? hostInfo.hostnames
: hostInfo.hostname
? [hostInfo.hostname]
: []
const hostnames = host.hostnameInfo[addressInfo.internalPort]
return {
...addressInfo,
hostnames: hostnameInfo.flatMap((h) => stringifyHostname(h)),
hostnames,
get onionHostnames() {
return hostnameInfo
.filter((h) => h.kind === "onion")
.map((h) => stringifyHostname(h))
return hostnames.filter((h) => h.kind === "onion")
},
get localHostnames() {
return hostnameInfo
.filter((h) => h.kind === "ip" && h.hostname.kind === "local")
.map((h) => stringifyHostname(h))
return hostnames.filter(
(h) => h.kind === "ip" && h.hostname.kind === "local",
)
},
get ipHostnames() {
return hostnameInfo
.filter(
(h) =>
h.kind === "ip" &&
(h.hostname.kind === "ipv4" || h.hostname.kind === "ipv6"),
)
.map((h) => stringifyHostname(h))
return hostnames.filter(
(h) =>
h.kind === "ip" &&
(h.hostname.kind === "ipv4" || h.hostname.kind === "ipv6"),
)
},
get ipv4Hostnames() {
return hostnameInfo
.filter((h) => h.kind === "ip" && h.hostname.kind === "ipv4")
.map((h) => stringifyHostname(h))
return hostnames.filter(
(h) => h.kind === "ip" && h.hostname.kind === "ipv4",
)
},
get ipv6Hostnames() {
return hostnameInfo
.filter((h) => h.kind === "ip" && h.hostname.kind === "ipv6")
.map((h) => stringifyHostname(h))
return hostnames.filter(
(h) => h.kind === "ip" && h.hostname.kind === "ipv6",
)
},
get nonIpHostnames() {
return hostnameInfo
.filter(
(h) =>
h.kind === "ip" &&
h.hostname.kind !== "ipv4" &&
h.hostname.kind !== "ipv6",
)
.map((h) => stringifyHostname(h))
return hostnames.filter(
(h) =>
h.kind === "ip" &&
h.hostname.kind !== "ipv4" &&
h.hostname.kind !== "ipv6",
)
},
get urls() {
return this.hostnames.map(toUrl)
return this.hostnames.flatMap(toUrl)
},
get onionUrls() {
return this.onionHostnames.map(toUrl)
return this.onionHostnames.flatMap(toUrl)
},
get localUrls() {
return this.localHostnames.map(toUrl)
return this.localHostnames.flatMap(toUrl)
},
get ipUrls() {
return this.ipHostnames.map(toUrl)
return this.ipHostnames.flatMap(toUrl)
},
get ipv4Urls() {
return this.ipv4Hostnames.map(toUrl)
return this.ipv4Hostnames.flatMap(toUrl)
},
get ipv6Urls() {
return this.ipv6Hostnames.map(toUrl)
return this.ipv6Hostnames.flatMap(toUrl)
},
get nonIpUrls() {
return this.nonIpHostnames.map(toUrl)
return this.nonIpHostnames.flatMap(toUrl)
},
}
}
@@ -193,23 +191,25 @@ const makeInterfaceFilled = async ({
packageId,
callback,
})
const hostInfo = await effects.getHostInfo({
packageId,
kind: null,
serviceInterfaceId: serviceInterfaceValue.id,
callback,
})
const primaryUrl = await effects.getPrimaryUrl({
serviceInterfaceId: id,
const hostId = serviceInterfaceValue.addressInfo.hostId
const host = await effects.getHostInfo({
packageId,
hostId,
callback,
})
const primaryUrl = await effects
.getPrimaryUrl({
serviceInterfaceId: id,
packageId,
callback,
})
.catch((e) => null)
const interfaceFilled: ServiceInterfaceFilled = {
...serviceInterfaceValue,
primaryUrl: primaryUrl,
hostInfo,
addressInfo: filledAddress(hostInfo, serviceInterfaceValue.addressInfo),
host,
addressInfo: filledAddress(host, serviceInterfaceValue.addressInfo),
get primaryHostname() {
if (primaryUrl == null) return null
return getHostname(primaryUrl)

View File

@@ -18,41 +18,27 @@ const makeManyInterfaceFilled = async ({
packageId,
callback,
})
const hostIdsRecord = Object.fromEntries(
await Promise.all(
Array.from(new Set(serviceInterfaceValues.map((x) => x.id))).map(
async (id) =>
[
id,
await effects.getHostInfo({
kind: null,
packageId,
serviceInterfaceId: id,
callback,
}),
] as const,
),
),
)
const serviceInterfacesFilled: ServiceInterfaceFilled[] = await Promise.all(
serviceInterfaceValues.map(async (serviceInterfaceValue) => {
const hostInfo = await effects.getHostInfo({
kind: null,
packageId,
serviceInterfaceId: serviceInterfaceValue.id,
callback,
})
const primaryUrl = await effects.getPrimaryUrl({
serviceInterfaceId: serviceInterfaceValue.id,
Object.values(serviceInterfaceValues).map(async (serviceInterfaceValue) => {
const hostId = serviceInterfaceValue.addressInfo.hostId
const host = await effects.getHostInfo({
packageId,
hostId,
callback,
})
const primaryUrl = await effects
.getPrimaryUrl({
serviceInterfaceId: serviceInterfaceValue.id,
packageId,
callback,
})
.catch(() => null)
return {
...serviceInterfaceValue,
primaryUrl: primaryUrl,
hostInfo,
addressInfo: filledAddress(hostInfo, serviceInterfaceValue.addressInfo),
host,
addressInfo: filledAddress(host, serviceInterfaceValue.addressInfo),
get primaryHostname() {
if (primaryUrl == null) return null
return getHostname(primaryUrl)

View File

@@ -10,6 +10,8 @@ import "./once"
export { GetServiceInterface, getServiceInterface } from "./getServiceInterface"
export { getServiceInterfaces } from "./getServiceInterfaces"
export { addressHostToUrl } from "./getServiceInterface"
export { hostnameInfoToAddress } from "./Hostname"
// prettier-ignore
export type FlattenIntersection<T> =
T extends ArrayLike<any> ? T :