FieldBrowserHosts.kt
src/jvmMain/kotlin/afield/app/server/FieldBrowserHosts.kt · 37 lines
@file:afield.runtime.completion.CompletionCategory("Internal")
package afield.app.server
import kotlinx.serialization.json.*
import java.nio.file.Files
import java.nio.file.Path
private val registeredFieldBrowserHosts by lazy {
readFieldBrowserHosts(Path.of(System.getProperty("user.home"), ".afield", "browser-hosts.json"),
System.getenv("AFIELD_BROWSER_HOSTS"))
}
internal fun fieldBrowserHosts(): Set<String> = registeredFieldBrowserHosts
Exact additional browser authorities, configured by the local operator, never by a request.
internal fun readFieldBrowserHosts(path: Path, environment: String? = null): Set<String>
Invalid or oversized files fail closed; an explicit environment list can still be used.
internal fun readFieldBrowserHosts(path: Path, environment: String? = null): Set<String> {
val saved = runCatching {
if (!Files.isRegularFile(path) || Files.size(path) > 16_384) return@runCatching emptyList<String>()
val entries = Json.parseToJsonElement(Files.readString(path)).jsonArray
require(entries.size <= 128)
entries.map { require(it.jsonPrimitive.isString); it.jsonPrimitive.content }
}.getOrDefault(emptyList())
return (saved + environment.orEmpty().split(',')).map(String::trim).filter(::validFieldDnsAuthority)
.map(String::lowercase).toSet()
}
internal fun validFieldDnsAuthority(authority: String): Boolean
DNS names only; localhost and literal IPs already have their own validation. Ports are exact.
internal fun validFieldDnsAuthority(authority: String): Boolean {
if (authority.count { it == ':' } > 1) return false
if (':' in authority && authority.substringAfter(':').toIntOrNull()?.let { it in 1..65535 } != true) return false
val host = authority.substringBefore(':')
return host.length in 1..253 && host.split('.').all {
it.length in 1..63 && it.matches(Regex("[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?"))
}
}