mirror of
https://github.com/abue-ammar/tinycast.git
synced 2026-10-02 08:14:38 +08:00
* Run extensions that speak WebSocket A bundled `ws` handshakes through `http.request` and wants a raw socket back, which the fetch-backed shim had none of. Sockets are `URLSessionWebSocketTask` now, and the upgrade path hands `ws` one that re-frames RFC 6455 both ways. Home Assistant is the reference case. * Resolve a .local host without joining a multicast group Home Assistant's default `homeassistant.local` is resolved by the extension itself with multicast-dns, which died on `dgram.createSocket`. `dgram` now answers an address query out of `getaddrinfo` — mDNSResponder handles `.local` — so nothing multicasts and no entitlement is needed. * Harden the WebSocket and dgram shims A rejected host call poisoned the send queue, so every later send and the close after it rejected too; the queue now recovers while the caller still sees the failure. A ping is answered by the peer through `URLSessionWebSocketTask`, not by a pong the adapter invents. An upgrade no listener claims destroys the socket, the way Node does, so the native task goes with it. `dgram` answers address questions only. And a socket id that is not a whole number falls back instead of trapping.
37 lines
1.5 KiB
Swift
37 lines
1.5 KiB
Swift
import Foundation
|
|
|
|
/// Host-name lookups for the `dgram` shim: the system resolver answers `.local` through Bonjour.
|
|
enum ExtensionNameResolver {
|
|
static func resolve(_ name: RenderValue?) async -> [String] {
|
|
let host = name?.stringValue ?? ""
|
|
guard !host.isEmpty else { return [] }
|
|
return await Task.detached(priority: .userInitiated) { addresses(of: host) }.value
|
|
}
|
|
|
|
/// Blocking, hence the detached task.
|
|
private static func addresses(of host: String) -> [String] {
|
|
var hints = addrinfo()
|
|
hints.ai_family = AF_INET
|
|
hints.ai_socktype = SOCK_STREAM
|
|
var head: UnsafeMutablePointer<addrinfo>?
|
|
guard getaddrinfo(host, nil, &hints, &head) == 0, let first = head else { return [] }
|
|
defer { freeaddrinfo(first) }
|
|
|
|
var found: [String] = []
|
|
var entry: UnsafeMutablePointer<addrinfo>? = first
|
|
while let current = entry {
|
|
var text = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
if getnameinfo(
|
|
current.pointee.ai_addr, current.pointee.ai_addrlen, &text, socklen_t(text.count),
|
|
nil, 0, NI_NUMERICHOST) == 0
|
|
{
|
|
let digits = text.prefix { $0 != 0 }.map { UInt8(bitPattern: $0) }
|
|
let address = String(decoding: digits, as: UTF8.self)
|
|
if !found.contains(address) { found.append(address) }
|
|
}
|
|
entry = current.pointee.ai_next
|
|
}
|
|
return found
|
|
}
|
|
}
|