Skip to content

Commit 07cc5a8

Browse files
Minor validation logic improvements (#782)
Throws different type of error if server validation failed --------- Co-authored-by: Błażej Pankowski <86720177+pblazej@users.noreply.github.com>
1 parent ddef539 commit 07cc5a8

4 files changed

Lines changed: 75 additions & 26 deletions

File tree

.changes/validation-logic

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="changed" "Minor validation logic improvements"

Sources/LiveKit/Core/SignalClient.swift

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -162,33 +162,47 @@ actor SignalClient: Loggable {
162162
}
163163

164164
return connectResponse
165-
} catch {
165+
} catch let connectionError {
166166
// Skip validation if user cancelled
167-
if error is CancellationError {
168-
await cleanUp(withError: error)
169-
throw error
167+
if connectionError is CancellationError {
168+
await cleanUp(withError: connectionError)
169+
throw connectionError
170170
}
171171

172172
// Skip validation if reconnect mode
173173
if reconnectMode != nil {
174-
await cleanUp(withError: error)
175-
throw error
174+
await cleanUp(withError: connectionError)
175+
throw LiveKitError(.network, internalError: connectionError)
176176
}
177177

178-
await cleanUp(withError: error)
178+
await cleanUp(withError: connectionError)
179179

180-
// Validate...
180+
// Attempt to validate with server
181181
let validateUrl = try Utils.buildUrl(url,
182182
connectOptions: connectOptions,
183183
participantSid: participantSid,
184184
adaptiveStream: adaptiveStream,
185185
validate: true)
186-
187186
log("Validating with url: \(validateUrl)...")
188-
let validationResponse = try await HTTP.requestValidation(from: validateUrl, token: token)
189-
log("Validate response: \(validationResponse)")
190-
// re-throw with validation response
191-
throw LiveKitError(.network, message: "Validation response: \"\(validationResponse)\"")
187+
do {
188+
try await HTTP.requestValidation(from: validateUrl, token: token)
189+
// Re-throw original error since validation passed
190+
throw LiveKitError(.network, internalError: connectionError)
191+
} catch let validationError as LiveKitError where validationError.type == .validation {
192+
// Re-throw validation error
193+
throw validationError
194+
} catch {
195+
let validationMessage = if let liveKitError = error as? LiveKitError {
196+
liveKitError.message ?? liveKitError.localizedDescription
197+
} else {
198+
error.localizedDescription
199+
}
200+
201+
// Preserve validation request failure details while keeping the original connection error.
202+
throw LiveKitError(.network,
203+
message: "Validation request failed: \(validationMessage)",
204+
internalError: connectionError)
205+
}
192206
}
193207
}
194208

Sources/LiveKit/Errors.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public enum LiveKitErrorType: Int, Sendable {
3030
case webRTC = 201
3131

3232
case network // Network issue
33+
case validation // Validation issue
3334

3435
// Server
3536
case duplicateIdentity = 500
@@ -80,6 +81,8 @@ extension LiveKitErrorType: CustomStringConvertible {
8081
"WebRTC error"
8182
case .network:
8283
"Network error"
84+
case .validation:
85+
"Validation error"
8386
case .duplicateIdentity:
8487
"Duplicate Participant identity"
8588
case .serverShutdown:
@@ -121,29 +124,40 @@ extension LiveKitErrorType: CustomStringConvertible {
121124
public class LiveKitError: NSError, @unchecked Sendable, Loggable {
122125
public let type: LiveKitErrorType
123126
public let message: String?
124-
public let underlyingError: Error?
127+
public let internalError: Error?
128+
129+
@available(*, deprecated, renamed: "internalError")
130+
public var underlyingError: Error? { internalError }
125131

126132
override public var underlyingErrors: [Error] {
127-
[underlyingError].compactMap { $0 }
133+
[internalError].compactMap { $0 }
128134
}
129135

130136
public init(_ type: LiveKitErrorType,
131137
message: String? = nil,
132138
internalError: Error? = nil)
133139
{
134140
func _computeDescription() -> String {
141+
var suffix = ""
135142
if let message {
136-
return "\(String(describing: type))(\(message))"
143+
suffix = "(\(message))"
144+
} else if let internalError {
145+
suffix = "(\(internalError.localizedDescription))"
137146
}
138-
return String(describing: type)
147+
return String(describing: type) + suffix
139148
}
140149

141150
self.type = type
142151
self.message = message
143-
underlyingError = internalError
152+
self.internalError = internalError
153+
154+
var userInfo: [String: Any] = [NSLocalizedDescriptionKey: _computeDescription()]
155+
if let internalError {
156+
userInfo[NSUnderlyingErrorKey] = internalError as NSError
157+
}
144158
super.init(domain: "io.livekit.swift-sdk",
145159
code: type.rawValue,
146-
userInfo: [NSLocalizedDescriptionKey: _computeDescription()])
160+
userInfo: userInfo)
147161
}
148162

149163
@available(*, unavailable)

Sources/LiveKit/Support/HTTP.swift

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,40 @@ class HTTP: NSObject {
2323
delegate: nil,
2424
delegateQueue: operationQueue)
2525

26-
static func requestValidation(from url: URL, token: String) async throws -> String {
27-
// let data = try await requestData(from: url, token: token)
26+
static func requestValidation(from url: URL, token: String) async throws {
2827
var request = URLRequest(url: url,
2928
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
3029
timeoutInterval: .defaultHTTPConnect)
3130
// Attach token to header
3231
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
32+
3333
// Make the data request
34-
let (data, _) = try await session.data(for: request)
35-
// Convert to string
36-
guard let string = String(data: data, encoding: .utf8) else {
37-
throw LiveKitError(.failedToConvertData, message: "Failed to convert string")
34+
let (data, response) = try await session.data(for: request)
35+
36+
guard let httpResponse = response as? HTTPURLResponse else {
37+
throw URLError(.badServerResponse)
3838
}
3939

40-
return string
40+
guard (200 ..< 300).contains(httpResponse.statusCode) else {
41+
let statusCode = httpResponse.statusCode
42+
let rawBody = String(data: data, encoding: .utf8)?
43+
.trimmingCharacters(in: .whitespacesAndNewlines)
44+
45+
let body = if let rawBody, !rawBody.isEmpty {
46+
rawBody.count > 1024 ? String(rawBody.prefix(1024)) + "..." : rawBody
47+
} else {
48+
"(No server message)"
49+
}
50+
51+
let details = "HTTP \(statusCode): \(body)"
52+
53+
// Treat request/token/permissions issues as validation errors.
54+
if (400 ..< 500).contains(statusCode), statusCode != 429 {
55+
throw LiveKitError(.validation, message: details)
56+
}
57+
58+
// Treat server/rate-limit issues as network errors.
59+
throw LiveKitError(.network, message: "Validation endpoint error: \(details)")
60+
}
4161
}
4262
}

0 commit comments

Comments
 (0)