// An origin for the tests: an HTTP/1.1 server on the loopback interface serving fixed bodies by // path. Nothing here leaves the host. import Foundation import Network final class TestOrigin: @unchecked Sendable { private let queue = DispatchQueue(label: "wai.sink.test-origin") private var listener: NWListener? private let lock = NSLock() private var files: [String: (Data, String)] = [:] private(set) var port: UInt16 = 0 private(set) var requests: [String] = [] func put(_ path: String, _ body: Data, type: String = "application/octet-stream") { lock.lock(); files[path] = (body, type); lock.unlock() } func url(_ path: String) -> URL { URL(string: "http://127.0.0.1:\(port)\(path)")! } func start() throws { let params = NWParameters.tcp params.requiredLocalEndpoint = NWEndpoint.hostPort(host: .ipv4(.loopback), port: .any) let l = try NWListener(using: params) let ready = DispatchSemaphore(value: 0) l.stateUpdateHandler = { s in if case .ready = s { ready.signal() } } l.newConnectionHandler = { [weak self] c in self?.serve(c) } l.start(queue: queue) _ = ready.wait(timeout: .now() + 5) port = l.port?.rawValue ?? 0 listener = l } func stop() { listener?.cancel() } private func serve(_ c: NWConnection) { c.start(queue: queue) c.receive(minimumIncompleteLength: 1, maximumLength: 65_536) { [weak self] d, _, _, _ in guard let self, let d else { return c.cancel() } let line = String(decoding: d, as: UTF8.self).components(separatedBy: "\r\n").first ?? "" let path = line.split(separator: " ").dropFirst().first.map(String.init) ?? "/" self.lock.lock() self.requests.append(path) let f = self.files[path] self.lock.unlock() let (status, body, type) = f.map { (200, $0.0, $0.1) } ?? (404, Data(), "text/plain") var out = Data("HTTP/1.1 \(status) \(status == 200 ? "OK" : "Not Found")\r\nContent-Type: \(type)\r\nContent-Length: \(body.count)\r\nConnection: close\r\n\r\n".utf8) out.append(body) c.send(content: out, completion: .contentProcessed { _ in c.cancel() }) } } } /// The repository's directory `wai/`, found from this file's path. func waiDir() -> URL { URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() } func hexData(_ s: String) -> Data { var d = Data(capacity: s.count / 2) var i = s.startIndex while i < s.endIndex { let j = s.index(i, offsetBy: 2) d.append(UInt8(s[i.. String { String(decoding: try! JSONSerialization.data(withJSONObject: v, options: [.sortedKeys, .fragmentsAllowed]), as: UTF8.self) } /// The test bundle, where the tests are built outside the package (the device host, TestHost/). final class TestBundleMarker {} /// The native sink corpus: the copy staged into the test bundle's resources (a device has no /// repository), else the repository's. func corpus() throws -> [String: Any] { #if SWIFT_PACKAGE let staged = Bundle.module.url(forResource: "vectors", withExtension: "json", subdirectory: "Resources") #else let staged = Bundle(for: TestBundleMarker.self).url(forResource: "vectors", withExtension: "json") #endif let d = try Data(contentsOf: staged ?? waiDir().appendingPathComponent("live-conformance/native-sink/vectors.json")) return try JSONSerialization.jsonObject(with: d) as! [String: Any] }