mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-06 20:54:18 +00:00
97 lines
2.9 KiB
Swift
97 lines
2.9 KiB
Swift
import Foundation
|
|
|
|
/// 持久化的订阅失败白名单,不携带 URL、秘密或任意平台错误描述。
|
|
enum SubscriptionRefreshFailure: Error, Codable, Equatable, Sendable {
|
|
enum TransportCategory: String, Codable, Equatable, Sendable {
|
|
case timedOut, tls, connection, unknown
|
|
}
|
|
enum ContentTypeCategory: String, Codable, Equatable, Sendable {
|
|
case missing, unsupported
|
|
}
|
|
enum RecordField: String, Codable, Equatable, Sendable {
|
|
case address, port, method, password, identity
|
|
}
|
|
case invalidURL
|
|
case unsupportedScheme
|
|
case insecureRedirect
|
|
case transport(TransportCategory)
|
|
case httpStatus(code: Int)
|
|
case contentType(ContentTypeCategory)
|
|
case decodingFailure
|
|
case unsupportedSchemaVersion
|
|
case missingServers
|
|
case recordValidation(index: Int, field: RecordField)
|
|
case duplicateIdentity
|
|
case commit
|
|
case unknown
|
|
}
|
|
|
|
extension SubscriptionRefreshFailure {
|
|
/// The only conversion point for transport failures entering durable state.
|
|
static func from(fetchError: SubscriptionFetchError) -> Self {
|
|
switch fetchError {
|
|
case .invalidURL:
|
|
.invalidURL
|
|
case .unsupportedScheme:
|
|
.unsupportedScheme
|
|
case .insecureRedirect:
|
|
.insecureRedirect
|
|
case .transport(let detail):
|
|
.transport(transportCategory(for: detail))
|
|
case .httpStatus(let code):
|
|
.httpStatus(code: code)
|
|
case .contentType(let received):
|
|
.contentType(received == nil ? .missing : .unsupported)
|
|
}
|
|
}
|
|
|
|
/// The only conversion point for parser failures entering durable state.
|
|
static func from(parseError: SubscriptionParseError) -> Self {
|
|
switch parseError {
|
|
case .decodingFailure:
|
|
.decodingFailure
|
|
case .unsupportedSchemaVersion:
|
|
.unsupportedSchemaVersion
|
|
case .missingServers:
|
|
.missingServers
|
|
case .recordValidation(let index, let reason):
|
|
.recordValidation(index: index, field: recordField(for: reason))
|
|
case .duplicateServerID:
|
|
.duplicateIdentity
|
|
}
|
|
}
|
|
|
|
private static func transportCategory(for detail: String) -> TransportCategory {
|
|
let normalized = detail.lowercased()
|
|
if normalized.contains("timedout") || normalized.contains("timeout") {
|
|
return .timedOut
|
|
}
|
|
if normalized.contains("tls") || normalized.contains("secure") {
|
|
return .tls
|
|
}
|
|
if normalized.contains("connection") || normalized.contains("network")
|
|
|| normalized.contains("cannotconnect")
|
|
{
|
|
return .connection
|
|
}
|
|
return .unknown
|
|
}
|
|
|
|
private static func recordField(for reason: String) -> RecordField {
|
|
switch reason {
|
|
case let value where value.contains("server_port"):
|
|
.port
|
|
case let value where value.contains("server"):
|
|
.address
|
|
case let value where value.contains("method"):
|
|
.method
|
|
case let value where value.contains("password"):
|
|
.password
|
|
case let value where value.contains("id"):
|
|
.identity
|
|
default:
|
|
.identity
|
|
}
|
|
}
|
|
}
|