106 lines
3.4 KiB
Swift
106 lines
3.4 KiB
Swift
import Foundation
|
|
import CryptoKit
|
|
|
|
struct Client {
|
|
|
|
let server: URL
|
|
|
|
private let delegate = NeverCacheDelegate()
|
|
|
|
init(server: URL) {
|
|
self.server = server
|
|
}
|
|
|
|
private enum RequestReponse: Error {
|
|
case requestFailed
|
|
case unknownResponse
|
|
case success(UInt8)
|
|
}
|
|
|
|
func deviceStatus() async throws -> ClientState {
|
|
let url = server.appendingPathComponent("status")
|
|
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)
|
|
let response = await integerReponse(to: request)
|
|
switch response {
|
|
case .requestFailed:
|
|
return .statusRequestFailed
|
|
case .unknownResponse:
|
|
return .unknownDeviceStatus
|
|
case .success(let int):
|
|
switch int {
|
|
case 0:
|
|
return .deviceDisconnected
|
|
case 1:
|
|
return .deviceConnected
|
|
default:
|
|
print("Unexpected device status '\(int)'")
|
|
return .unknownDeviceStatus
|
|
}
|
|
}
|
|
}
|
|
|
|
func keyResponse(key: SymmetricKey, id: Int) async throws -> ClientState {
|
|
let url = server.appendingPathComponent("key/\(id)")
|
|
var request = URLRequest(url: url)
|
|
request.httpBody = key.data
|
|
request.httpMethod = "POST"
|
|
let response = await integerReponse(to: request)
|
|
switch response {
|
|
case .requestFailed:
|
|
return .statusRequestFailed
|
|
case .unknownResponse:
|
|
return .unknownDeviceStatus
|
|
case .success(let int):
|
|
guard let status = KeyResult(rawValue: int) else {
|
|
print("Invalid key response: \(int)")
|
|
return .unknownDeviceStatus
|
|
}
|
|
return ClientState(keyResult: status)
|
|
}
|
|
}
|
|
|
|
private func fulfill(_ request: URLRequest) async -> Result<Data, RequestReponse> {
|
|
do {
|
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
guard let code = (response as? HTTPURLResponse)?.statusCode else {
|
|
print("No response from server")
|
|
return .failure(.requestFailed)
|
|
}
|
|
guard code == 200 else {
|
|
print("Invalid server response \(code)")
|
|
return .failure(.requestFailed)
|
|
}
|
|
return .success(data)
|
|
} catch {
|
|
print("Request failed: \(error)")
|
|
return .failure(.requestFailed)
|
|
}
|
|
}
|
|
|
|
private func integerReponse(to request: URLRequest) async -> RequestReponse {
|
|
let response = await fulfill(request)
|
|
switch response {
|
|
case .failure(let cause):
|
|
return cause
|
|
case .success(let data):
|
|
guard let string = String(data: data, encoding: .utf8) else {
|
|
print("Unexpected device status data: \([UInt8](data))")
|
|
return .unknownResponse
|
|
}
|
|
guard let int = UInt8(string) else {
|
|
print("Unexpected device status '\(string)'")
|
|
return .unknownResponse
|
|
}
|
|
return .success(int)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class NeverCacheDelegate: NSObject, NSURLConnectionDataDelegate {
|
|
|
|
func connection(_ connection: NSURLConnection, willCacheResponse cachedResponse: CachedURLResponse) -> CachedURLResponse? {
|
|
return nil
|
|
}
|
|
}
|