1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 | import Foundation
import IrdP2PCrypto
/// Every failure the SDK throws, tagged by kind.
public enum P2PError: Error, Sendable, Equatable {
/// A misconfiguration detected before any request (blank room, mailbox disabled, ...).
case config(String)
/// The network layer failed (DNS, TLS, socket) — `detail` is the underlying description.
case transport(String)
/// A request or handshake exceeded its deadline.
case timeout
/// A non-2xx HTTP response. `code` is the node's error code when the body was `{"error":"<code>"}`.
case http(status: Int, code: String?, body: Data?)
/// The WebSocket upgrade was refused: 403 origin not allowed, 404 unknown app host, 429 too many connections.
case upgradeRejected(status: Int)
/// The relay answered with an `error` frame.
case relay(RelayErrorCode)
/// A frame would exceed the node's `max_frame_bytes`; sending it would close the connection.
case frameTooLarge(bytes: Int, limit: Int)
/// A send was attempted before the room was joined.
case notJoined
/// The addressed peer is not a verified member of the room.
case peerUnknown(PeerId)
/// The room or session has been closed.
case closed
/// A cryptographic operation failed on the local side (never used for remote tampering, which is dropped silently).
case crypto(String)
/// The mailbox error code, when this is an HTTP error from the mailbox node.
public var mailboxCode: MailboxErrorCode? {
if case .http(_, let code?, _) = self { return MailboxErrorCode(code) }
return nil
}
}
/// Error codes of the relay's `{"t":"error","code":...}` frames.
public enum RelayErrorCode: Sendable, Hashable {
case badMessage, notJoined, tooManyRooms, roomFull, rateLimited
case other(String)
public init(_ raw: String) {
switch raw {
case "bad-message": self = .badMessage
case "not-joined": self = .notJoined
case "too-many-rooms": self = .tooManyRooms
case "room-full": self = .roomFull
case "rate-limited": self = .rateLimited
default: self = .other(raw)
}
}
public var rawValue: String {
switch self {
case .badMessage: return "bad-message"
case .notJoined: return "not-joined"
case .tooManyRooms: return "too-many-rooms"
case .roomFull: return "room-full"
case .rateLimited: return "rate-limited"
case .other(let s): return s
}
}
}
/// Error codes of the mailbox node (`{"error":"<code>"}` bodies).
public enum MailboxErrorCode: Sendable, Hashable {
case unknownApp, originNotAllowed, notFound, mailboxNotConfigured
case missingToken, tokenExpired, invalidToken, tokenOutOfScope
case rateLimited, unreadableBody, emptyBody, badLimit, badCursor
case messageTooLarge, nodeStorageFull, storageUnavailable, storageError, methodNotAllowed
case other(String)
public init(_ raw: String) {
switch raw {
case "unknown_app": self = .unknownApp
case "origin_not_allowed": self = .originNotAllowed
case "not_found": self = .notFound
case "mailbox_not_configured": self = .mailboxNotConfigured
case "missing_token": self = .missingToken
case "token_expired": self = .tokenExpired
case "invalid_token": self = .invalidToken
case "token_out_of_scope": self = .tokenOutOfScope
case "rate_limited": self = .rateLimited
case "unreadable_body": self = .unreadableBody
case "empty_body": self = .emptyBody
case "bad_limit": self = .badLimit
case "bad_cursor": self = .badCursor
case "message_too_large": self = .messageTooLarge
case "node_storage_full": self = .nodeStorageFull
case "storage_unavailable": self = .storageUnavailable
case "storage_error": self = .storageError
case "method_not_allowed": self = .methodNotAllowed
default: self = .other(raw)
}
}
/// Whether a later retry can succeed without changing the request.
public var isRetryable: Bool {
switch self {
case .rateLimited, .nodeStorageFull, .storageUnavailable, .storageError: return true
default: return false
}
}
/// Whether the failure is about the bearer token (re-mint once, then give up).
public var isAuth: Bool {
switch self {
case .missingToken, .tokenExpired, .invalidToken, .tokenOutOfScope: return true
default: return false
}
}
}
|