init
Some checks failed
ci / macos (push) Has been cancelled
ci / ios (push) Has been cancelled
ci / check-linter (push) Has been cancelled

This commit is contained in:
wenzuhuai
2026-01-12 18:29:52 +08:00
commit d7bdb4f378
87 changed files with 12664 additions and 0 deletions

View File

@@ -0,0 +1,244 @@
// Copyright 2024 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public protocol NatsErrorProtocol: Error, CustomStringConvertible {}
public enum NatsError {
public enum ServerError: NatsErrorProtocol, Equatable {
case staleConnection
case maxConnectionsExceeded
case authorizationViolation
case authenticationExpired
case authenticationRevoked
case authenticationTimeout
case permissionsViolation(Operation, String, String?)
case proto(String)
public var description: String {
switch self {
case .staleConnection:
return "nats: stale connection"
case .maxConnectionsExceeded:
return "nats: maximum connections exceeded"
case .authorizationViolation:
return "nats: authorization violation"
case .authenticationExpired:
return "nats: authentication expired"
case .authenticationRevoked:
return "nats: authentication revoked"
case .authenticationTimeout:
return "nats: authentication timeout"
case .permissionsViolation(let operation, let subject, let queue):
if let queue {
return
"nats: permissions violation for operation \"\(operation)\" on subject \"\(subject)\" using queue \"\(queue)\""
} else {
return
"nats: permissions violation for operation \"\(operation)\" on subject \"\(subject)\""
}
case .proto(let error):
return "nats: \(error)"
}
}
var normalizedError: String {
return description.trimWhitespacesAndApostrophes().lowercased()
}
init(_ error: String) {
let normalizedError = error.trimWhitespacesAndApostrophes().lowercased()
if normalizedError.contains("stale connection") {
self = .staleConnection
} else if normalizedError.contains("maximum connections exceeded") {
self = .maxConnectionsExceeded
} else if normalizedError.contains("authorization violation") {
self = .authorizationViolation
} else if normalizedError.contains("authentication expired") {
self = .authenticationExpired
} else if normalizedError.contains("authentication revoked") {
self = .authenticationRevoked
} else if normalizedError.contains("authentication timeout") {
self = .authenticationTimeout
} else if normalizedError.contains("permissions violation") {
if let (operation, subject, queue) = NatsError.ServerError.parsePermissions(
error: error)
{
self = .permissionsViolation(operation, subject, queue)
} else {
self = .proto(error)
}
} else {
self = .proto(error)
}
}
public enum Operation: String, Equatable {
case publish = "Publish"
case subscribe = "Subscription"
}
internal static func parsePermissions(error: String) -> (Operation, String, String?)? {
let pattern = "(Publish|Subscription) to \"(\\S+)\""
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(
in: error, options: [], range: NSRange(location: 0, length: error.utf16.count))
guard let match = matches.first else {
return nil
}
var operation: Operation?
if let operationRange = Range(match.range(at: 1), in: error) {
let operationString = String(error[operationRange])
operation = Operation(rawValue: operationString)
}
var subject: String?
if let subjectRange = Range(match.range(at: 2), in: error) {
subject = String(error[subjectRange])
}
let queuePattern = "using queue \"(\\S+)\""
let queueRegex = try! NSRegularExpression(pattern: queuePattern)
let queueMatches = queueRegex.matches(
in: error, options: [], range: NSRange(location: 0, length: error.utf16.count))
var queue: String?
if let match = queueMatches.first, let queueRange = Range(match.range(at: 1), in: error)
{
queue = String(error[queueRange])
}
if let operation, let subject {
return (operation, subject, queue)
} else {
return nil
}
}
}
public enum ProtocolError: NatsErrorProtocol, Equatable {
case invalidOperation(String)
case parserFailure(String)
public var description: String {
switch self {
case .invalidOperation(let op):
return "nats: unknown server operation: \(op)"
case .parserFailure(let cause):
return "nats: parser failure: \(cause)"
}
}
}
public enum ClientError: NatsErrorProtocol {
case internalError(String)
case maxReconnects
case connectionClosed
case io(Error)
case invalidConnection(String)
case cancelled
case alreadyConnected
public var description: String {
switch self {
case .internalError(let error):
return "nats: internal error: \(error)"
case .maxReconnects:
return "nats: max reconnects exceeded"
case .connectionClosed:
return "nats: connection is closed"
case .io(let error):
return "nats: IO error: \(error)"
case .invalidConnection(let error):
return "nats: \(error)"
case .cancelled:
return "nats: operation cancelled"
case .alreadyConnected:
return "nats: client is already connected or connecting"
}
}
}
public enum ConnectError: NatsErrorProtocol {
case invalidConfig(String)
case tlsFailure(Error)
case timeout
case dns(Error)
case io(Error)
public var description: String {
switch self {
case .invalidConfig(let error):
return "nats: invalid client configuration: \(error)"
case .tlsFailure(let error):
return "nats: TLS error: \(error)"
case .timeout:
return "nats: timed out waiting for connection"
case .dns(let error):
return "nats: DNS lookup error: \(error)"
case .io(let error):
return "nats: error establishing connection: \(error)"
}
}
}
public enum RequestError: NatsErrorProtocol, Equatable {
case noResponders
case timeout
case permissionDenied
public var description: String {
switch self {
case .noResponders:
return "nats: no responders available for request"
case .timeout:
return "nats: request timed out"
case .permissionDenied:
return "nats: permission denied"
}
}
}
public enum SubscriptionError: NatsErrorProtocol, Equatable {
case invalidSubject
case invalidQueue
case permissionDenied
case subscriptionClosed
public var description: String {
switch self {
case .invalidSubject:
return "nats: invalid subject name"
case .invalidQueue:
return "nats: invalid queue group name"
case .permissionDenied:
return "nats: permission denied"
case .subscriptionClosed:
return "nats: subscription closed"
}
}
}
public enum ParseHeaderError: NatsErrorProtocol, Equatable {
case invalidCharacter
public var description: String {
switch self {
case .invalidCharacter:
return
"nats: invalid header name (name cannot contain non-ascii alphanumeric characters other than '-')"
}
}
}
}