diff --git a/.changes/validation-logic b/.changes/validation-logic new file mode 100644 index 000000000..cf748497e --- /dev/null +++ b/.changes/validation-logic @@ -0,0 +1 @@ +patch type="changed" "Minor validation logic improvements" diff --git a/Sources/LiveKit/Core/SignalClient.swift b/Sources/LiveKit/Core/SignalClient.swift index 5df326a08..408fe2435 100644 --- a/Sources/LiveKit/Core/SignalClient.swift +++ b/Sources/LiveKit/Core/SignalClient.swift @@ -162,33 +162,39 @@ actor SignalClient: Loggable { } return connectResponse - } catch { + } catch let connectionError { // Skip validation if user cancelled - if error is CancellationError { - await cleanUp(withError: error) - throw error + if connectionError is CancellationError { + await cleanUp(withError: connectionError) + throw connectionError } // Skip validation if reconnect mode if reconnectMode != nil { - await cleanUp(withError: error) - throw error + await cleanUp(withError: connectionError) + throw LiveKitError(.network, internalError: connectionError) } - await cleanUp(withError: error) + await cleanUp(withError: connectionError) - // Validate... + // Attempt to validate with server let validateUrl = try Utils.buildUrl(url, connectOptions: connectOptions, participantSid: participantSid, adaptiveStream: adaptiveStream, validate: true) - log("Validating with url: \(validateUrl)...") - let validationResponse = try await HTTP.requestValidation(from: validateUrl, token: token) - log("Validate response: \(validationResponse)") - // re-throw with validation response - throw LiveKitError(.network, message: "Validation response: \"\(validationResponse)\"") + do { + try await HTTP.requestValidation(from: validateUrl, token: token) + // Re-throw original error since validation passed + throw LiveKitError(.network, internalError: connectionError) + } catch let validationError as LiveKitError where validationError.type == .validation { + // Re-throw validation error + throw validationError + } catch { + // Re-throw original connection error for network issues during validation + throw LiveKitError(.network, internalError: connectionError) + } } } diff --git a/Sources/LiveKit/Errors.swift b/Sources/LiveKit/Errors.swift index 2d5d79393..ff90c7d47 100644 --- a/Sources/LiveKit/Errors.swift +++ b/Sources/LiveKit/Errors.swift @@ -30,6 +30,7 @@ public enum LiveKitErrorType: Int, Sendable { case webRTC = 201 case network // Network issue + case validation // Network issue // Server case duplicateIdentity = 500 @@ -80,6 +81,8 @@ extension LiveKitErrorType: CustomStringConvertible { "WebRTC error" case .network: "Network error" + case .validation: + "Validation error" case .duplicateIdentity: "Duplicate Participant identity" case .serverShutdown: @@ -121,10 +124,10 @@ extension LiveKitErrorType: CustomStringConvertible { public class LiveKitError: NSError, @unchecked Sendable, Loggable { public let type: LiveKitErrorType public let message: String? - public let underlyingError: Error? + public let internalError: Error? override public var underlyingErrors: [Error] { - [underlyingError].compactMap { $0 } + [internalError].compactMap { $0 } } public init(_ type: LiveKitErrorType, @@ -132,15 +135,18 @@ public class LiveKitError: NSError, @unchecked Sendable, Loggable { internalError: Error? = nil) { func _computeDescription() -> String { + var suffix = "" if let message { - return "\(String(describing: type))(\(message))" + suffix = "(\(message))" + } else if let internalError { + suffix = "(\(internalError.localizedDescription))" } - return String(describing: type) + return String(describing: type) + suffix } self.type = type self.message = message - underlyingError = internalError + self.internalError = internalError super.init(domain: "io.livekit.swift-sdk", code: type.rawValue, userInfo: [NSLocalizedDescriptionKey: _computeDescription()]) diff --git a/Sources/LiveKit/Support/HTTP.swift b/Sources/LiveKit/Support/HTTP.swift index f0a15e1db..1f1625ed1 100644 --- a/Sources/LiveKit/Support/HTTP.swift +++ b/Sources/LiveKit/Support/HTTP.swift @@ -23,20 +23,24 @@ class HTTP: NSObject { delegate: nil, delegateQueue: operationQueue) - static func requestValidation(from url: URL, token: String) async throws -> String { - // let data = try await requestData(from: url, token: token) + static func requestValidation(from url: URL, token: String) async throws { var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: .defaultHTTPConnect) // Attach token to header request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization") + // Make the data request - let (data, _) = try await session.data(for: request) - // Convert to string - guard let string = String(data: data, encoding: .utf8) else { - throw LiveKitError(.failedToConvertData, message: "Failed to convert string") + let (data, response) = try await session.data(for: request) + + guard let httpResponse = response as? HTTPURLResponse else { + throw URLError(.badServerResponse) } - return string + // For non-2xx status codes, throw validation error with response body + if !(200 ..< 300).contains(httpResponse.statusCode) { + let message = String(data: data, encoding: .utf8) + throw LiveKitError(.validation, message: message ?? "(No server message)") + } } }