p2p-swift / Sources / IrdP2PSignaling / AppDescriptor.swift
612 words
  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
import Foundation

/// The app's self-description at `GET https://<host>/.well-known/ap2p`.
/// `relay` and `mailbox` are present only when those features are enabled —
/// feature-detect from their presence rather than probing.
public struct AppDescriptor: Sendable, Equatable, Decodable {
    public struct Endpoints: Sendable, Equatable, Decodable {
        public var ws: URL
        public var turnCredentials: URL
        enum CodingKeys: String, CodingKey { case ws, turnCredentials = "turn_credentials" }
        public init(ws: URL, turnCredentials: URL) { self.ws = ws; self.turnCredentials = turnCredentials }
    }

    public struct Relay: Sendable, Equatable, Decodable {
        public var peerId: String
        public var multiaddrs: [String]
        public var pubsubDiscoveryTopic: String?
        enum CodingKeys: String, CodingKey { case peerId = "peer_id", multiaddrs, pubsubDiscoveryTopic = "pubsub_discovery_topic" }
    }

    public struct Mailbox: Sendable, Equatable, Decodable {
        public var endpoint: URL
        public var ttlHours: Int
        public var maxMB: Int
        public var maxMessageKB: Int
        public var openMint: Bool
        /// Present only while `openMint` is true.
        public var mintEndpoint: URL?
        public var mintIPPerMin: Int?
        public var mintAppPerMin: Int?
        enum CodingKeys: String, CodingKey {
            case endpoint, ttlHours = "ttl_hours", maxMB = "max_mb", maxMessageKB = "max_message_kb", openMint = "open_mint"
            case mintEndpoint = "mint_endpoint", mintIPPerMin = "mint_ip_per_min", mintAppPerMin = "mint_app_per_min"
        }
        public init(endpoint: URL, ttlHours: Int, maxMB: Int, maxMessageKB: Int, openMint: Bool, mintEndpoint: URL?, mintIPPerMin: Int? = nil, mintAppPerMin: Int? = nil) {
            self.endpoint = endpoint; self.ttlHours = ttlHours; self.maxMB = maxMB; self.maxMessageKB = maxMessageKB
            self.openMint = openMint; self.mintEndpoint = mintEndpoint; self.mintIPPerMin = mintIPPerMin; self.mintAppPerMin = mintAppPerMin
        }
    }

    public struct Limits: Sendable, Equatable, Decodable {
        public var maxRooms: Int
        public var maxRoomPeers: Int
        public var maxConnsPerIP: Int
        public var maxBytesPerSec: Int
        public var messagesPerSec: Int
        public var messageBurst: Int
        public var maxFrameBytes: Int
        enum CodingKeys: String, CodingKey {
            case maxRooms = "max_rooms", maxRoomPeers = "max_room_peers", maxConnsPerIP = "max_conns_per_ip"
            case maxBytesPerSec = "max_bytes_per_sec", messagesPerSec = "messages_per_sec", messageBurst = "message_burst", maxFrameBytes = "max_frame_bytes"
        }
        /// The node-wide defaults, used when the descriptor cannot be fetched.
        public static let defaults = Limits(maxRooms: 500, maxRoomPeers: 16, maxConnsPerIP: 20, maxBytesPerSec: 262_144, messagesPerSec: 30, messageBurst: 60, maxFrameBytes: 65_536)
        public init(maxRooms: Int, maxRoomPeers: Int, maxConnsPerIP: Int, maxBytesPerSec: Int, messagesPerSec: Int, messageBurst: Int, maxFrameBytes: Int) {
            self.maxRooms = maxRooms; self.maxRoomPeers = maxRoomPeers; self.maxConnsPerIP = maxConnsPerIP
            self.maxBytesPerSec = maxBytesPerSec; self.messagesPerSec = messagesPerSec; self.messageBurst = messageBurst; self.maxFrameBytes = maxFrameBytes
        }
    }

    public var protocolName: String
    public var version: String
    public var spec: URL?
    public var endpoints: Endpoints
    public var relay: Relay?
    public var mailbox: Mailbox?
    public var limits: Limits
    /// True when the descriptor was synthesized because the fetch failed.
    public var isFallback: Bool = false

    enum CodingKeys: String, CodingKey { case protocolName = "protocol", version, spec, endpoints, relay, mailbox, limits }

    public init(from decoder: any Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        protocolName = try c.decodeIfPresent(String.self, forKey: .protocolName) ?? "ap2p-signal"
        version = try c.decodeIfPresent(String.self, forKey: .version) ?? "1"
        spec = try? c.decodeIfPresent(URL.self, forKey: .spec)
        endpoints = try c.decode(Endpoints.self, forKey: .endpoints)
        relay = try? c.decodeIfPresent(Relay.self, forKey: .relay)
        mailbox = try? c.decodeIfPresent(Mailbox.self, forKey: .mailbox)
        limits = try c.decodeIfPresent(Limits.self, forKey: .limits) ?? .defaults
    }

    public init(protocolName: String = "ap2p-signal", version: String = "1", spec: URL? = nil, endpoints: Endpoints, relay: Relay? = nil, mailbox: Mailbox? = nil, limits: Limits = .defaults, isFallback: Bool = false) {
        self.protocolName = protocolName; self.version = version; self.spec = spec; self.endpoints = endpoints
        self.relay = relay; self.mailbox = mailbox; self.limits = limits; self.isFallback = isFallback
    }

    public static func decode(_ data: Data) throws -> AppDescriptor {
        try JSONDecoder().decode(AppDescriptor.self, from: data)
    }

    /// What the descriptor of `host` would say if only the documented default
    /// paths and node-wide limits were known. No mailbox (cannot be detected offline).
    public static func fallback(host: String) -> AppDescriptor {
        AppDescriptor(
            endpoints: Endpoints(ws: URL(string: "wss://\(host)/ws")!, turnCredentials: URL(string: "https://\(host)/turn-credentials")!),
            limits: .defaults, isFallback: true
        )
    }
}

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/p2p-swift.git