From c9f742ab394ab223678de7392888fd0f342d22aa Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 19 Aug 2024 19:44:06 +0800 Subject: [PATCH 01/89] change preimage size to data count --- Blockchain/Sources/Blockchain/Runtime.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Blockchain/Sources/Blockchain/Runtime.swift b/Blockchain/Sources/Blockchain/Runtime.swift index 537370da..35052560 100644 --- a/Blockchain/Sources/Blockchain/Runtime.swift +++ b/Blockchain/Sources/Blockchain/Runtime.swift @@ -129,8 +129,8 @@ public final class Runtime { item.blocks += 1 item.tickets += UInt32(block.extrinsic.tickets.tickets.count) item.preimages += UInt32(block.extrinsic.preimages.preimages.count) - // try to change preimage size to serviceIndices - item.preimagesBytes += UInt32(block.extrinsic.preimages.preimages.reduce(into: 0) { $0 += $1.serviceIndices }) + // try to change preimage size to data count + item.preimagesBytes += UInt32(block.extrinsic.preimages.preimages.reduce(into: 0) { $0 += $1.data.count }) acc[block.header.authorIndex] = item for report in block.extrinsic.reports.guarantees { From 15b60fb0f7196de388b10e387a05ccb070d32cde Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Thu, 29 Aug 2024 09:54:57 +0800 Subject: [PATCH 02/89] update --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6ac8b77e..b1fb8657 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ target # static lib files .lib/ tasks.json +contents.xcworkspacedata From e05af5cc2505982947b38fbc0d81e51bd8e9a860 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 1 Sep 2024 06:57:05 +0800 Subject: [PATCH 03/89] update ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b1fb8657..66df06dd 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ target .lib/ tasks.json contents.xcworkspacedata +Package.resolved From 3469c60c500eba3282802cffedd5205594c0ef5e Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 10:50:50 +0800 Subject: [PATCH 04/89] update msquic --- .github/workflows/ci.yml | 19 ++++ Makefile | 5 +- Networking/Package.swift | 65 +++++++++++ .../Sources/Networking/Networking.swift | 15 +++ .../Sources/Networking/assets/server.cert | 21 ++++ .../Sources/Networking/assets/server.key | 28 +++++ .../Sources/Networking/msquic/Alpn.swift | 31 ++++++ .../Sources/Networking/msquic/QuicError.swift | 103 ++++++++++++++++++ .../Networking/msquic/QuicMessage.swift | 39 +++++++ .../Networking/msquic/QuicStatus.swift | 92 ++++++++++++++++ .../Sources/Networking/msquic/Typealias.swift | 20 ++++ Networking/Sources/module.modulemap | 4 + .../Tests/NetworkingTests/AlpnTest.swift | 17 +++ .../Tests/NetworkingTests/QuicErrorTest.swift | 35 ++++++ .../NetworkingTests/QuicMessageTest.swift | 33 ++++++ .../NetworkingTests/QuicStatusTest.swift | 20 ++++ scripts/msquic.sh | 33 ++++++ 17 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 Networking/Package.swift create mode 100644 Networking/Sources/Networking/Networking.swift create mode 100644 Networking/Sources/Networking/assets/server.cert create mode 100644 Networking/Sources/Networking/assets/server.key create mode 100644 Networking/Sources/Networking/msquic/Alpn.swift create mode 100644 Networking/Sources/Networking/msquic/QuicError.swift create mode 100644 Networking/Sources/Networking/msquic/QuicMessage.swift create mode 100644 Networking/Sources/Networking/msquic/QuicStatus.swift create mode 100644 Networking/Sources/Networking/msquic/Typealias.swift create mode 100644 Networking/Sources/module.modulemap create mode 100644 Networking/Tests/NetworkingTests/AlpnTest.swift create mode 100644 Networking/Tests/NetworkingTests/QuicErrorTest.swift create mode 100644 Networking/Tests/NetworkingTests/QuicMessageTest.swift create mode 100644 Networking/Tests/NetworkingTests/QuicStatusTest.swift create mode 100755 scripts/msquic.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a33c2fd1..d577bb8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,18 @@ jobs: uses: actions/checkout@v4 with: submodules: recursive + - name: MsQuic Install Dependencies + run: | + export DEBIAN_FRONTEND=noninteractive + sudo apt-add-repository -y ppa:lttng/stable-2.13 + sudo apt-get update + sudo apt-get install -y lttng-tools lttng-modules-dkms babeltrace2 liblttng-ust-dev python3-babeltrace + sudo apt-get install -y cmake + sudo apt-get install -y build-essential + - name: Get msquic submodule commit hash + id: msquic-commit-hash + run: | + echo "commit-hash=$(git submodule status Networking/Sources/msquic/ | cut -c2-41)" >> $GITHUB_OUTPUT - name: Get blst submodule commit hash id: blst-commit-hash run: | @@ -56,6 +68,13 @@ jobs: ~/.cargo/git/db/ Utils/Sources/bandersnatch/target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Cache msquic static lib + uses: actions/cache@v4 + with: + path: .lib/libmsquic.a + key: ${{ runner.os }}-libs-msquic-${{ steps.msquic-commit-hash.outputs.commit-hash }} + restore-keys: | + ${{ runner.os }}-libs-msquic - name: Cache blst static lib uses: actions/cache@v4 with: diff --git a/Makefile b/Makefile index 47059cad..09274c09 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ default: build githooks: .git/hooks/pre-commit .PHONY: deps -deps: .lib/libblst.a .lib/libbandersnatch_vrfs.a .lib/librocksdb.a .lib/libec.a +deps: .lib/libblst.a .lib/libbandersnatch_vrfs.a .lib/librocksdb.a .lib/libec.a .lib/libmsquic.a .lib/libblst.a: ./scripts/blst.sh @@ -22,6 +22,9 @@ deps: .lib/libblst.a .lib/libbandersnatch_vrfs.a .lib/librocksdb.a .lib/libec.a .lib/librocksdb.a: ./scripts/rocksdb.sh +.lib/libmsquic.a: + ./scripts/msquic.sh + .PHONY: test test: githooks deps ./scripts/runTests.sh test diff --git a/Networking/Package.swift b/Networking/Package.swift new file mode 100644 index 00000000..d539ea6f --- /dev/null +++ b/Networking/Package.swift @@ -0,0 +1,65 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Networking", + platforms: [ + .macOS(.v14), + ], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "Networking", + targets: ["Networking"] + ), + ], + dependencies: [ + .package(path: "../Utils"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"), + .package(url: "https://github.com/apple/swift-testing.git", branch: "0.10.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"), + .package(url: "https://github.com/apple/swift-atomics.git", from: "1.2.0"), + + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "Networking", + + dependencies: [ + "msquic", + "Utils", + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "Logging", package: "swift-log"), + .product(name: "Atomics", package: "swift-atomics"), + + ], + resources: [ + .process("assets"), + ], + swiftSettings: [ + .define("DEBUG_ASSERT", .when(configuration: .debug)), + ], + linkerSettings: [ + .unsafeFlags(["-L../.lib"]), + ] + ), + .systemLibrary( + name: "msquic", + path: "Sources" + ), + .testTarget( + name: "NetworkingTests", + dependencies: [ + "Networking", + .product(name: "Testing", package: "swift-testing"), + ] + ), + ], + swiftLanguageVersions: [.version("6")] +) diff --git a/Networking/Sources/Networking/Networking.swift b/Networking/Sources/Networking/Networking.swift new file mode 100644 index 00000000..f103a9be --- /dev/null +++ b/Networking/Sources/Networking/Networking.swift @@ -0,0 +1,15 @@ +import msquic + +public class Networking { + public class Config { + public let listenAddress: String + public let port: Int + + public init(listenAddress: String, port: Int) { + self.listenAddress = listenAddress + self.port = port + } + } + + init() {} +} diff --git a/Networking/Sources/Networking/assets/server.cert b/Networking/Sources/Networking/assets/server.cert new file mode 100644 index 00000000..2e4b1063 --- /dev/null +++ b/Networking/Sources/Networking/assets/server.cert @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUYaN9BKGKso1vf23K1JQINstBQ5IwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA5MDMwMTQ3MjFaFw0yNDEw +MDMwMTQ3MjFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQC8OtKwzLpWXCHOaFu6pPgNiKoXtIo45h4aF83HLULc +GJsCx2AuSdhNQjbG+G9kTsUp+kTEaXpyDdMm/KGNDzg9Iunj1swzXpp3Q9MmELTu +gkkkHKuFnhKNspqdZGWMMqtvqktVu0dLaxebSkwqH+i3eR9L1PHJcb4Tj6rdw14o +Aw4ZnqcMiv+iGq/usg/FmIWgkBUWko3pHEBdudKj1fKzlzkUlvDZQ3hN74GNGMdy +BxUHUQr17mr4/nb0CH2JcSHfV0z1eW6I+fKTY0E/zlY1yCUXzpgwlGvgc0h2kDhr +9i9IYDDUZtmtLSsC75LkkmvKben111r4AD08r1/6HiyjAgMBAAGjUzBRMB0GA1Ud +DgQWBBT/Zhl2dPN0OtpQ/KbT8tr3zxenuzAfBgNVHSMEGDAWgBT/Zhl2dPN0OtpQ +/KbT8tr3zxenuzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAM +8Q0Ods8Y2rNwXd9Dcu2jx09PCQG+Urn0upaNiAG9eowsZiJZYZLE31kBZXLQBqnu +frGgcYWXFlMXpKU3qYXeVeq8Vug1gnp3k3T8fsPCZxrMyOgXz7/EV1rjh3k+xIsC +zg5pJDk9pHmWfElpbfE0GuJZR8hdK4h9xabNq/h/g9jagwfFw2kvtP65bGXzEULF +B/4QQmFb8hZqYcOn9Q6QkW7FKHj2NWbippBqFGwcr6/61CTmuZXHvKijg8q8syCI +rF5PC773w9DWgZ0lY2YgvSdG5YUiVpfWzoOq8mfmnQ4QEVulBSm1NIf/7nDvXlm/ +qe+ux+F3fBiIOBvnH8N3 +-----END CERTIFICATE----- diff --git a/Networking/Sources/Networking/assets/server.key b/Networking/Sources/Networking/assets/server.key new file mode 100644 index 00000000..0584d6bc --- /dev/null +++ b/Networking/Sources/Networking/assets/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8OtKwzLpWXCHO +aFu6pPgNiKoXtIo45h4aF83HLULcGJsCx2AuSdhNQjbG+G9kTsUp+kTEaXpyDdMm +/KGNDzg9Iunj1swzXpp3Q9MmELTugkkkHKuFnhKNspqdZGWMMqtvqktVu0dLaxeb +SkwqH+i3eR9L1PHJcb4Tj6rdw14oAw4ZnqcMiv+iGq/usg/FmIWgkBUWko3pHEBd +udKj1fKzlzkUlvDZQ3hN74GNGMdyBxUHUQr17mr4/nb0CH2JcSHfV0z1eW6I+fKT +Y0E/zlY1yCUXzpgwlGvgc0h2kDhr9i9IYDDUZtmtLSsC75LkkmvKben111r4AD08 +r1/6HiyjAgMBAAECggEAMjLl2azfzQRJJ8kBHae9V66o3iWR71a/lGngwUu61wPg +4z7U0cNz4jbUcP/0hSXUwzsxd8hBlYVGWNFrLDlw4C0df1vbnZ+PRltaRn6SIkGw +y+axf7MGc6+8OFl8E4W33tHlwXlurBhldU69PUbBQ50T0Z961CR3crQP95CQ3c5t +fuoWSHeGoDoGeCSMyfHI1WimYm5L3ruGO41ZpNyE5goNiocRUiAdMNOyJzjHegoG +Ry4Z1fqRdKNoQRHurEjR8rHj336IU8l9AmDWV1bNgGP4nLSdC8jQ6qJ7Z8Ln0QFN +Qc/sVlmyz7AKraNzSPql9T7NzfVc6jb6KyEVipgqEQKBgQDodmkQ/sn56R1vM0CX +YZ8zHgIGciwSF+oWITWmmdWCOHe8ECbBnvH+KKRBweUKQI8ZR3BglemDxtOu5ohD +qKQi7d3D3/SHJHGDNpH0welnp8XoDz3IuXTN3+2sXWf9YVCiW0wnHEghBCfX8Lph +K7EpR8dttJJ2r4vFV7ofZ5g0JwKBgQDPSd5rRsNKkStSc9z3KuafESLfo4pzxoq0 +3nWf7EQVKdj74YpJoix1pmElj+0/XRg+/actBmcp0nN53DJHUiWE0uTrOlXywQvH +Cpd2QywiGDm03XxO6uUx21PPNnJSPmSLF/KGAnAzPDXjdz2li1QqlCgvBl8CWEZ4 +oApOBeMlJQKBgFmTt2zrIZ2BrOv6zfsHjTc/avxc5oqaWIPqjCgEw3eIlusLNkQK +IaoUsyb7tmy67+mH09AENJWo+k09XGjmBUnIa1pdZHbnHi63yoqMZoqg5exEY0SD +4nSqw4VWAQpsg4vsrm2rwkemuEvH4aosEfqcREcL/BxsDOogogIUA1ylAoGAJyaS +hbx1wi2s+wO7JkvKBx0bcxXWXouPQx4JJU1ghobC3r/4eHPS5d2o14lZi/AvViZe +Xtiov3NqMAfwzxuqzm1uoyb/eLwRV2Seus1xFndw3sI+akFfTI1aRPbJnNL/9m1t +dvYa7vJeUzoAQLYSDYCyHj+bzjuSA9JmX9oozqkCgYBHUEaRb/8DNjoNXVSeubD0 +wNYBv1w47HdnTmBD0aX/1H2W1b5/DnmaTDjHy0T04DzgoYgSmtLFbPj9BB+/zfzU +sBSngmt7mOBh8e361jiX+nEpx95ffNPG3p2cbq0ZkFtQrFFI46a6B9J53sI9S9w6 +9vu7dtndG49uP6SdCXu44w== +-----END PRIVATE KEY----- diff --git a/Networking/Sources/Networking/msquic/Alpn.swift b/Networking/Sources/Networking/msquic/Alpn.swift new file mode 100644 index 00000000..28fa9488 --- /dev/null +++ b/Networking/Sources/Networking/msquic/Alpn.swift @@ -0,0 +1,31 @@ +import Foundation +import msquic + +struct Alpn { + private let protocolName: Data + private let version: Int + private let genesisHeader: Data + private let headerPrefixLength: Int = 4 + init(_ protocolName: Data = Data("jamnp-s".utf8), version: Int = 0, genesisHeader: Data) + throws(QuicError) + { + self.protocolName = protocolName + self.version = version + guard genesisHeader.count >= headerPrefixLength else { + throw QuicError.invalidAlpn + } + self.genesisHeader = genesisHeader + } + + var alpnString: String { + let protocolString = data2String(data: protocolName) + let headerPrefix = data2String(data: genesisHeader.prefix(headerPrefixLength)) + return "\(protocolString)/\(version)/\(headerPrefix)" + } + + private func data2String(data: Data) -> String { + let uint8Array = [UInt8](data) + let characters = uint8Array.map { Character(UnicodeScalar($0)) } + return String(characters) + } +} diff --git a/Networking/Sources/Networking/msquic/QuicError.swift b/Networking/Sources/Networking/msquic/QuicError.swift new file mode 100644 index 00000000..172f8f34 --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicError.swift @@ -0,0 +1,103 @@ +import Foundation + +public enum QuicError: Error, Equatable, Sendable, Codable { + case invalidStatus(status: QuicStatusCode) + case invalidAlpn + case getApiFailed + case getRegistrationFailed + case getConnectionFailed + case getStreamFailed + case getClientFailed + case messageNotFound + case sendFailed + + enum CodingKeys: String, CodingKey { + case type + case status + } + + enum ErrorType: String, Codable { + case invalidStatus + case invalidAlpn + case getApiFailed + case getRegistrationFailed + case getConnectionFailed + case getStreamFailed + case getClientFailed + case messageNotFound + case sendFailed + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case let .invalidStatus(status): + try container.encode(ErrorType.invalidStatus, forKey: .type) + try container.encode(status, forKey: .status) + default: + let type = ErrorType(from: self) + try container.encode(type, forKey: .type) + } + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let type = try container.decode(ErrorType.self, forKey: .type) + + switch type { + case .invalidStatus: + let status = try container.decode(QuicStatusCode.self, forKey: .status) + self = .invalidStatus(status: status) + default: + self = type.toQuicError() + } + } +} + +extension QuicError.ErrorType { + init(from error: QuicError) { + switch error { + case .invalidAlpn: + self = .invalidAlpn + case .getApiFailed: + self = .getApiFailed + case .getRegistrationFailed: + self = .getRegistrationFailed + case .getConnectionFailed: + self = .getConnectionFailed + case .getStreamFailed: + self = .getStreamFailed + case .getClientFailed: + self = .getClientFailed + case .messageNotFound: + self = .messageNotFound + case .sendFailed: + self = .sendFailed + default: + fatalError("Unhandled case: \(error)") + } + } + + func toQuicError() -> QuicError { + switch self { + case .invalidAlpn: + .invalidAlpn + case .getApiFailed: + .getApiFailed + case .getRegistrationFailed: + .getRegistrationFailed + case .getConnectionFailed: + .getConnectionFailed + case .getStreamFailed: + .getStreamFailed + case .getClientFailed: + .getClientFailed + case .messageNotFound: + .messageNotFound + case .sendFailed: + .sendFailed + default: + fatalError("Unhandled case: \(self)") + } + } +} diff --git a/Networking/Sources/Networking/msquic/QuicMessage.swift b/Networking/Sources/Networking/msquic/QuicMessage.swift new file mode 100644 index 00000000..afb96a25 --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicMessage.swift @@ -0,0 +1,39 @@ +import Foundation + +enum QuicMessageType: String, Codable { + case data + case shutdown + case aborted + case unknown + case received + case close + case connected + case shutdownComplete +} + +public struct QuicMessage: @unchecked Sendable, Equatable, Codable { + let type: QuicMessageType + let data: Data? + + init(type: QuicMessageType, data: Data?) { + self.type = type + self.data = data + } + + enum CodingKeys: String, CodingKey { + case type + case data + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(type, forKey: .type) + try container.encode(data, forKey: .data) + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + type = try container.decode(QuicMessageType.self, forKey: .type) + data = try container.decode(Data?.self, forKey: .data) + } +} diff --git a/Networking/Sources/Networking/msquic/QuicStatus.swift b/Networking/Sources/Networking/msquic/QuicStatus.swift new file mode 100644 index 00000000..93865881 --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicStatus.swift @@ -0,0 +1,92 @@ +import Foundation + +extension UInt32? { + var status: QuicStatus { + QuicStatus(self) + } +} + +extension QuicStatus { + var isFailed: Bool { + // fix c unsigned int < 0 + Int32(bitPattern: self) > 0 + } + + var value: Int32 { + Int32(bitPattern: self) + } + + var isSucceeded: Bool { + self <= 0 + } + + init(_ value: UInt32?) { + guard let value else { + self = QuicStatusCode.unknown.rawValue + return + } + self = value + } + + var code: QuicStatusCode { + QuicStatusCode.from(rawValue: self) + } +} + +public enum QuicStatusCode: QuicStatus, Equatable, Sendable, Codable { + case success = 0 + case notFound = 2 + case outOfMemory = 12 + case invalidParameter = 22 + case invalidState = 1 + + #if os(macOS) + case notSupported = 102 + case bufferTooSmall = 84 + case handshakeFailure = 53 + case aborted = 89 + case addressInUse = 48 + case invalidAddress = 47 + case connectionTimeout = 60 + case connectionIdle = 101 + case connectionRefused = 61 + case protocolError = 100 + case verNegError = 43 + case unreachable = 65 + case userCanceled = 105 + case alpnNegFailure = 42 + case alpnInUse = 41 + #else + case notSupported = 95 + case bufferTooSmall = 75 + case handshakeFailure = 103 + case aborted = 125 + case addressInUse = 98 + case invalidAddress = 97 + case connectionTimeout = 110 + case connectionIdle = 62 + case connectionRefused = 111 + case protocolError = 71 + case verNegError = 93 + case unreachable = 113 + case userCanceled = 130 + case alpnNegFailure = 92 + case alpnInUse = 91 + #endif + + case internalError = 5 + case tlsError = 126 + case streamLimitReached = 86 + case unknown = 4_294_967_294 + + static func from(rawValue: UInt32) -> QuicStatusCode { + QuicStatusCode(rawValue: rawValue) ?? .unknown + } + + static func from(rawValue: UInt32?) -> QuicStatusCode { + guard let rawValue else { + return .unknown + } + return QuicStatusCode(rawValue: rawValue) ?? .unknown + } +} diff --git a/Networking/Sources/Networking/msquic/Typealias.swift b/Networking/Sources/Networking/msquic/Typealias.swift new file mode 100644 index 00000000..bb2c66b7 --- /dev/null +++ b/Networking/Sources/Networking/msquic/Typealias.swift @@ -0,0 +1,20 @@ +import msquic + +public typealias QuicStatus = UInt32 +public typealias QuicApiTable = QUIC_API_TABLE +public typealias HQuic = HQUIC +public typealias QuicBuffer = QUIC_BUFFER +public typealias QuicSettings = QUIC_SETTINGS +public typealias QuicCredentialConfig = QUIC_CREDENTIAL_CONFIG +public typealias QuicListenerEvent = QUIC_LISTENER_EVENT +public typealias QuicConnectionEvent = QUIC_CONNECTION_EVENT +public typealias QuicStreamEvent = QUIC_STREAM_EVENT +public typealias ConnectionCallback = @convention(c) ( + OpaquePointer?, UnsafeMutableRawPointer?, UnsafePointer? +) -> QuicStatus +public typealias StreamCallback = @convention(c) ( + OpaquePointer?, UnsafeMutableRawPointer?, UnsafeMutablePointer? +) -> QuicStatus +public typealias ServerListenerCallback = @convention(c) ( + OpaquePointer?, UnsafeMutableRawPointer?, UnsafeMutablePointer? +) -> QuicStatus diff --git a/Networking/Sources/module.modulemap b/Networking/Sources/module.modulemap new file mode 100644 index 00000000..e9debb2b --- /dev/null +++ b/Networking/Sources/module.modulemap @@ -0,0 +1,4 @@ +module msquic { + header "msquic/src/inc/msquic.h" + link "msquic" +} diff --git a/Networking/Tests/NetworkingTests/AlpnTest.swift b/Networking/Tests/NetworkingTests/AlpnTest.swift new file mode 100644 index 00000000..8383768e --- /dev/null +++ b/Networking/Tests/NetworkingTests/AlpnTest.swift @@ -0,0 +1,17 @@ +import Foundation +import Testing + +@testable import Networking + +struct AlpnTests { + @Test func invalidAlpn() throws { + #expect(throws: QuicError.invalidAlpn) { + try Alpn(version: 0, genesisHeader: Data("jam".utf8)) + } + } + + @Test func validAlpn() throws { + let alpn = try Alpn(version: 0, genesisHeader: Data("jamabcdefg".utf8)) + #expect(alpn.alpnString == "jamnp-s/0/jama") + } +} diff --git a/Networking/Tests/NetworkingTests/QuicErrorTest.swift b/Networking/Tests/NetworkingTests/QuicErrorTest.swift new file mode 100644 index 00000000..50b0b99b --- /dev/null +++ b/Networking/Tests/NetworkingTests/QuicErrorTest.swift @@ -0,0 +1,35 @@ +import Foundation +@testable import Networking +import Testing + +struct QuicErrorTests { + @Test func testEncodingAndDecodingInvalidStatus() throws { + let originalError = QuicError.invalidStatus( + status: QuicStatusCode.internalError + ) + let encodedData = try JSONEncoder().encode(originalError) + let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) + #expect(originalError == decodedError) + } + + @Test func testEncodingAndDecodingGetApiFailed() throws { + let originalError = QuicError.getApiFailed + let encodedData = try JSONEncoder().encode(originalError) + let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) + #expect(originalError == decodedError) + } + + @Test func testEncodingAndDecodingMessageNotFound() throws { + let originalError = QuicError.messageNotFound + let encodedData = try JSONEncoder().encode(originalError) + let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) + #expect(originalError == decodedError) + } + + @Test func testEncodingAndDecodingSendFailed() throws { + let originalError = QuicError.sendFailed + let encodedData = try JSONEncoder().encode(originalError) + let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) + #expect(originalError == decodedError) + } +} diff --git a/Networking/Tests/NetworkingTests/QuicMessageTest.swift b/Networking/Tests/NetworkingTests/QuicMessageTest.swift new file mode 100644 index 00000000..34fc6ff9 --- /dev/null +++ b/Networking/Tests/NetworkingTests/QuicMessageTest.swift @@ -0,0 +1,33 @@ +import Foundation +@testable import Networking +import Testing + +struct QuicMessageTests { + @Test func testEncodingAndDecodingDataMessage() throws { + let originalMessage = QuicMessage(type: .data, data: "Test data".data(using: .utf8)) + let encodedData = try JSONEncoder().encode(originalMessage) + let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) + #expect(originalMessage == decodedMessage) + } + + @Test func testEncodingAndDecodingUnknownMessage() throws { + let originalMessage = QuicMessage(type: .unknown, data: nil) + let encodedData = try JSONEncoder().encode(originalMessage) + let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) + #expect(originalMessage == decodedMessage) + } + + @Test func testEncodingAndDecodingReceivedMessage() throws { + let originalMessage = QuicMessage(type: .received, data: "Received data".data(using: .utf8)) + let encodedData = try JSONEncoder().encode(originalMessage) + let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) + #expect(originalMessage == decodedMessage) + } + + @Test func testEncodingAndDecodingShutdownCompleteMessage() throws { + let originalMessage = QuicMessage(type: .shutdownComplete, data: nil) + let encodedData = try JSONEncoder().encode(originalMessage) + let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) + #expect(originalMessage == decodedMessage) + } +} diff --git a/Networking/Tests/NetworkingTests/QuicStatusTest.swift b/Networking/Tests/NetworkingTests/QuicStatusTest.swift new file mode 100644 index 00000000..4b43a66b --- /dev/null +++ b/Networking/Tests/NetworkingTests/QuicStatusTest.swift @@ -0,0 +1,20 @@ +import Testing + +@testable import Networking + +struct QuicStatusTests { + @Test func unknownStatus() throws { + #expect(QuicStatusCode.unknown == QuicStatusCode.from(rawValue: 888)) + } + + @Test func successStatus() throws { + #expect(QuicStatusCode.success == QuicStatusCode.from(rawValue: 0)) + #expect(QuicStatusCode.from(rawValue: 0).rawValue.isSucceeded) + } + + @Test func failureStatus() throws { + let status: QuicStatus = 2 + #expect(QuicStatusCode.notFound == QuicStatusCode.from(rawValue: status)) + #expect(status.isFailed) + } +} diff --git a/scripts/msquic.sh b/scripts/msquic.sh new file mode 100755 index 00000000..99ef3cf6 --- /dev/null +++ b/scripts/msquic.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -e + + +# Setup quic C module +CWD=$(pwd) + +mkdir -p .lib + +system=$(uname -s) + +arch=$(uname -m) + +cd Networking/Sources/msquic || { echo "Submodule directory not found"; exit 1; } + +rm -rf build + +mkdir build && cd build + +if [ $system = "Darwin" ]; then + cmake -DENABLE_LOGGING=OFF -DCMAKE_OSX_ARCHITECTURES=$arch -DCMAKE_C_FLAGS="-Wno-invalid-unevaluated-string" -DQUIC_BUILD_SHARED=off .. +fi + +if [ $system = "Linux" ]; then + cmake -G 'Unix Makefiles' -DENABLE_LOGGING=OFF -DCMAKE_OSX_ARCHITECTURES=$arch -DCMAKE_C_FLAGS="-Wno-invalid-unevaluated-string" -DQUIC_BUILD_SHARED=off .. +fi + +cmake --build . || { echo "Build msquic library failed"; exit 1; } + +cp bin/Release/libmsquic.a ${CWD}/.lib + +echo "Setup msquic successfully." From dec7e6dc66ae9148209a24f3ab9e149b6703e346 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 10:52:28 +0800 Subject: [PATCH 05/89] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fdf0afab..50bd3eb2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ JAM built with Swift - SwiftLint: `brew install swiftlint` - SwiftFormat: `brew install swiftformat` +- CMake (required by msquic): `brew install cmake` - Precommit hooks: `make githooks` - Pull submodules: `git submodule update --init --recursive` - Setup deps: `make deps` From af53deee5e3b554fca8594aff9509996d55ed234 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 10:56:40 +0800 Subject: [PATCH 06/89] update quic more test --- Networking/Tests/NetworkingTests/QuicMessageTest.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Networking/Tests/NetworkingTests/QuicMessageTest.swift b/Networking/Tests/NetworkingTests/QuicMessageTest.swift index 34fc6ff9..ced649f3 100644 --- a/Networking/Tests/NetworkingTests/QuicMessageTest.swift +++ b/Networking/Tests/NetworkingTests/QuicMessageTest.swift @@ -3,13 +3,6 @@ import Foundation import Testing struct QuicMessageTests { - @Test func testEncodingAndDecodingDataMessage() throws { - let originalMessage = QuicMessage(type: .data, data: "Test data".data(using: .utf8)) - let encodedData = try JSONEncoder().encode(originalMessage) - let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) - #expect(originalMessage == decodedMessage) - } - @Test func testEncodingAndDecodingUnknownMessage() throws { let originalMessage = QuicMessage(type: .unknown, data: nil) let encodedData = try JSONEncoder().encode(originalMessage) @@ -18,7 +11,7 @@ struct QuicMessageTests { } @Test func testEncodingAndDecodingReceivedMessage() throws { - let originalMessage = QuicMessage(type: .received, data: "Received data".data(using: .utf8)) + let originalMessage = QuicMessage(type: .received, data: Data("received".utf8)) let encodedData = try JSONEncoder().encode(originalMessage) let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) #expect(originalMessage == decodedMessage) From d73f276f10dd45e17a0fe19d526b3b1a7aeedb6a Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 11:08:34 +0800 Subject: [PATCH 07/89] update submodule --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index 4974b778..1f3640f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "Database/Sources/rocksdb"] path = Database/Sources/rocksdb url = https://github.com/facebook/rocksdb.git +[submodule "Networking/Sources/msquic"] + path = Networking/Sources/msquic + url = https://github.com/microsoft/msquic.git \ No newline at end of file From 5ba1ab31515fb6ebff512a45c58824e9fec22c28 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 11:22:39 +0800 Subject: [PATCH 08/89] update quic --- Networking/Tests/NetworkingTests/QuicStatusTest.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Networking/Tests/NetworkingTests/QuicStatusTest.swift b/Networking/Tests/NetworkingTests/QuicStatusTest.swift index 4b43a66b..e20e1ce7 100644 --- a/Networking/Tests/NetworkingTests/QuicStatusTest.swift +++ b/Networking/Tests/NetworkingTests/QuicStatusTest.swift @@ -17,4 +17,9 @@ struct QuicStatusTests { #expect(QuicStatusCode.notFound == QuicStatusCode.from(rawValue: status)) #expect(status.isFailed) } + + @Test func isFailed() throws { + #expect(QuicStatusCode.notFound.rawValue.isFailed) + #expect(!QuicStatusCode.success.rawValue.isFailed) + } } From a727ef3fc3d2c41baec83ebc6d80cdbb4bc1f982 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 11:39:32 +0800 Subject: [PATCH 09/89] update submodule --- Networking/Sources/msquic | 1 + 1 file changed, 1 insertion(+) create mode 160000 Networking/Sources/msquic diff --git a/Networking/Sources/msquic b/Networking/Sources/msquic new file mode 160000 index 00000000..2f1a7bec --- /dev/null +++ b/Networking/Sources/msquic @@ -0,0 +1 @@ +Subproject commit 2f1a7bec97b4b3a25bebed6da3c00bfa6f58e66f From 180926c56ba7fb522818d915100cbaa4fd3dfe31 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 14:53:06 +0800 Subject: [PATCH 10/89] update msquic --- Networking/Package.swift | 3 --- .../Sources/Networking/msquic/Alpn.swift | 26 ++++++------------ .../Sources/Networking/msquic/QuicError.swift | 18 ++++++++++--- .../Networking/msquic/QuicMessage.swift | 27 +------------------ .../Tests/NetworkingTests/AlpnTest.swift | 6 ++--- .../Tests/NetworkingTests/QuicErrorTest.swift | 12 ++++----- .../NetworkingTests/QuicMessageTest.swift | 11 ++------ 7 files changed, 33 insertions(+), 70 deletions(-) diff --git a/Networking/Package.swift b/Networking/Package.swift index d539ea6f..6bc3f89a 100644 --- a/Networking/Package.swift +++ b/Networking/Package.swift @@ -42,9 +42,6 @@ let package = Package( resources: [ .process("assets"), ], - swiftSettings: [ - .define("DEBUG_ASSERT", .when(configuration: .debug)), - ], linkerSettings: [ .unsafeFlags(["-L../.lib"]), ] diff --git a/Networking/Sources/Networking/msquic/Alpn.swift b/Networking/Sources/Networking/msquic/Alpn.swift index 28fa9488..5c445594 100644 --- a/Networking/Sources/Networking/msquic/Alpn.swift +++ b/Networking/Sources/Networking/msquic/Alpn.swift @@ -1,31 +1,21 @@ import Foundation -import msquic struct Alpn { - private let protocolName: Data - private let version: Int - private let genesisHeader: Data - private let headerPrefixLength: Int = 4 - init(_ protocolName: Data = Data("jamnp-s".utf8), version: Int = 0, genesisHeader: Data) - throws(QuicError) - { + private let protocolName: String + private let version: String + private let genesisHeader: String + + init(_ protocolName: String = "jamnp-s", version: String = "0", genesisHeader: String) throws { self.protocolName = protocolName self.version = version - guard genesisHeader.count >= headerPrefixLength else { + + guard genesisHeader.count >= 4 else { throw QuicError.invalidAlpn } self.genesisHeader = genesisHeader } var alpnString: String { - let protocolString = data2String(data: protocolName) - let headerPrefix = data2String(data: genesisHeader.prefix(headerPrefixLength)) - return "\(protocolString)/\(version)/\(headerPrefix)" - } - - private func data2String(data: Data) -> String { - let uint8Array = [UInt8](data) - let characters = uint8Array.map { Character(UnicodeScalar($0)) } - return String(characters) + "\(protocolName)/\(version)/\(genesisHeader.prefix(4))" } } diff --git a/Networking/Sources/Networking/msquic/QuicError.swift b/Networking/Sources/Networking/msquic/QuicError.swift index 172f8f34..b58b7680 100644 --- a/Networking/Sources/Networking/msquic/QuicError.swift +++ b/Networking/Sources/Networking/msquic/QuicError.swift @@ -10,6 +10,7 @@ public enum QuicError: Error, Equatable, Sendable, Codable { case getClientFailed case messageNotFound case sendFailed + case unknown // For handling unknown error types enum CodingKeys: String, CodingKey { case type @@ -26,8 +27,10 @@ public enum QuicError: Error, Equatable, Sendable, Codable { case getClientFailed case messageNotFound case sendFailed + case unknown // For handling unknown error types } + // Encode the QuicError to a Codable format public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { @@ -40,6 +43,7 @@ public enum QuicError: Error, Equatable, Sendable, Codable { } } + // Decode the QuicError from a Codable format public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(ErrorType.self, forKey: .type) @@ -55,8 +59,11 @@ public enum QuicError: Error, Equatable, Sendable, Codable { } extension QuicError.ErrorType { + // Initialize ErrorType from QuicError init(from error: QuicError) { switch error { + case .invalidStatus: + self = .invalidStatus case .invalidAlpn: self = .invalidAlpn case .getApiFailed: @@ -73,13 +80,16 @@ extension QuicError.ErrorType { self = .messageNotFound case .sendFailed: self = .sendFailed - default: - fatalError("Unhandled case: \(error)") + case .unknown: + self = .unknown } } + // Convert ErrorType back to QuicError func toQuicError() -> QuicError { switch self { + case .invalidStatus: + .invalidStatus(status: .unknown) // Provide a default status case .invalidAlpn: .invalidAlpn case .getApiFailed: @@ -96,8 +106,8 @@ extension QuicError.ErrorType { .messageNotFound case .sendFailed: .sendFailed - default: - fatalError("Unhandled case: \(self)") + case .unknown: + .unknown } } } diff --git a/Networking/Sources/Networking/msquic/QuicMessage.swift b/Networking/Sources/Networking/msquic/QuicMessage.swift index afb96a25..3c4a2f94 100644 --- a/Networking/Sources/Networking/msquic/QuicMessage.swift +++ b/Networking/Sources/Networking/msquic/QuicMessage.swift @@ -1,9 +1,6 @@ import Foundation enum QuicMessageType: String, Codable { - case data - case shutdown - case aborted case unknown case received case close @@ -11,29 +8,7 @@ enum QuicMessageType: String, Codable { case shutdownComplete } -public struct QuicMessage: @unchecked Sendable, Equatable, Codable { +public struct QuicMessage: Sendable, Equatable, Codable { let type: QuicMessageType let data: Data? - - init(type: QuicMessageType, data: Data?) { - self.type = type - self.data = data - } - - enum CodingKeys: String, CodingKey { - case type - case data - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(type, forKey: .type) - try container.encode(data, forKey: .data) - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - type = try container.decode(QuicMessageType.self, forKey: .type) - data = try container.decode(Data?.self, forKey: .data) - } } diff --git a/Networking/Tests/NetworkingTests/AlpnTest.swift b/Networking/Tests/NetworkingTests/AlpnTest.swift index 8383768e..419b0dba 100644 --- a/Networking/Tests/NetworkingTests/AlpnTest.swift +++ b/Networking/Tests/NetworkingTests/AlpnTest.swift @@ -6,12 +6,12 @@ import Testing struct AlpnTests { @Test func invalidAlpn() throws { #expect(throws: QuicError.invalidAlpn) { - try Alpn(version: 0, genesisHeader: Data("jam".utf8)) + try Alpn(genesisHeader: "jam") } } @Test func validAlpn() throws { - let alpn = try Alpn(version: 0, genesisHeader: Data("jamabcdefg".utf8)) - #expect(alpn.alpnString == "jamnp-s/0/jama") + let alpn = try Alpn(version: "1.2", genesisHeader: "jamabcdefg") + #expect(alpn.alpnString == "jamnp-s/1.2/jama") } } diff --git a/Networking/Tests/NetworkingTests/QuicErrorTest.swift b/Networking/Tests/NetworkingTests/QuicErrorTest.swift index 50b0b99b..f69fe723 100644 --- a/Networking/Tests/NetworkingTests/QuicErrorTest.swift +++ b/Networking/Tests/NetworkingTests/QuicErrorTest.swift @@ -3,30 +3,28 @@ import Foundation import Testing struct QuicErrorTests { - @Test func testEncodingAndDecodingInvalidStatus() throws { - let originalError = QuicError.invalidStatus( - status: QuicStatusCode.internalError - ) + @Test func invalidStatus() throws { + let originalError = QuicError.invalidStatus(status: QuicStatusCode.internalError) let encodedData = try JSONEncoder().encode(originalError) let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) #expect(originalError == decodedError) } - @Test func testEncodingAndDecodingGetApiFailed() throws { + @Test func getApiFailed() throws { let originalError = QuicError.getApiFailed let encodedData = try JSONEncoder().encode(originalError) let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) #expect(originalError == decodedError) } - @Test func testEncodingAndDecodingMessageNotFound() throws { + @Test func messageNotFound() throws { let originalError = QuicError.messageNotFound let encodedData = try JSONEncoder().encode(originalError) let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) #expect(originalError == decodedError) } - @Test func testEncodingAndDecodingSendFailed() throws { + @Test func sendFailed() throws { let originalError = QuicError.sendFailed let encodedData = try JSONEncoder().encode(originalError) let decodedError = try JSONDecoder().decode(QuicError.self, from: encodedData) diff --git a/Networking/Tests/NetworkingTests/QuicMessageTest.swift b/Networking/Tests/NetworkingTests/QuicMessageTest.swift index ced649f3..f14c6ab4 100644 --- a/Networking/Tests/NetworkingTests/QuicMessageTest.swift +++ b/Networking/Tests/NetworkingTests/QuicMessageTest.swift @@ -3,21 +3,14 @@ import Foundation import Testing struct QuicMessageTests { - @Test func testEncodingAndDecodingUnknownMessage() throws { - let originalMessage = QuicMessage(type: .unknown, data: nil) - let encodedData = try JSONEncoder().encode(originalMessage) - let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) - #expect(originalMessage == decodedMessage) - } - - @Test func testEncodingAndDecodingReceivedMessage() throws { + @Test func receivedMessage() throws { let originalMessage = QuicMessage(type: .received, data: Data("received".utf8)) let encodedData = try JSONEncoder().encode(originalMessage) let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) #expect(originalMessage == decodedMessage) } - @Test func testEncodingAndDecodingShutdownCompleteMessage() throws { + @Test func shutdownCompleteMessage() throws { let originalMessage = QuicMessage(type: .shutdownComplete, data: nil) let encodedData = try JSONEncoder().encode(originalMessage) let decodedMessage = try JSONDecoder().decode(QuicMessage.self, from: encodedData) From 08588ab2259ad98153795366dec841496226bd66 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 15:10:24 +0800 Subject: [PATCH 11/89] update alpn --- Networking/Sources/Networking/msquic/Alpn.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Networking/Sources/Networking/msquic/Alpn.swift b/Networking/Sources/Networking/msquic/Alpn.swift index 5c445594..935793cc 100644 --- a/Networking/Sources/Networking/msquic/Alpn.swift +++ b/Networking/Sources/Networking/msquic/Alpn.swift @@ -5,6 +5,8 @@ struct Alpn { private let version: String private let genesisHeader: String + lazy var alpnString: String = "\(protocolName)/\(version)/\(genesisHeader.prefix(4))" + init(_ protocolName: String = "jamnp-s", version: String = "0", genesisHeader: String) throws { self.protocolName = protocolName self.version = version @@ -14,8 +16,4 @@ struct Alpn { } self.genesisHeader = genesisHeader } - - var alpnString: String { - "\(protocolName)/\(version)/\(genesisHeader.prefix(4))" - } } From 62a5ed33bd95e2a4e510c3fabe6c8b4eec2883a0 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 15:24:36 +0800 Subject: [PATCH 12/89] update alpn --- Networking/Sources/Networking/msquic/Alpn.swift | 6 +++--- Networking/Tests/NetworkingTests/AlpnTest.swift | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Networking/Sources/Networking/msquic/Alpn.swift b/Networking/Sources/Networking/msquic/Alpn.swift index 935793cc..720a706b 100644 --- a/Networking/Sources/Networking/msquic/Alpn.swift +++ b/Networking/Sources/Networking/msquic/Alpn.swift @@ -4,14 +4,14 @@ struct Alpn { private let protocolName: String private let version: String private let genesisHeader: String - - lazy var alpnString: String = "\(protocolName)/\(version)/\(genesisHeader.prefix(4))" + private static let headerPrefixLength = 4 + lazy var alpnString: String = "\(protocolName)/\(version)/\(genesisHeader.prefix(Alpn.headerPrefixLength))" init(_ protocolName: String = "jamnp-s", version: String = "0", genesisHeader: String) throws { self.protocolName = protocolName self.version = version - guard genesisHeader.count >= 4 else { + guard genesisHeader.count >= Alpn.headerPrefixLength else { throw QuicError.invalidAlpn } self.genesisHeader = genesisHeader diff --git a/Networking/Tests/NetworkingTests/AlpnTest.swift b/Networking/Tests/NetworkingTests/AlpnTest.swift index 419b0dba..abb083c3 100644 --- a/Networking/Tests/NetworkingTests/AlpnTest.swift +++ b/Networking/Tests/NetworkingTests/AlpnTest.swift @@ -11,7 +11,7 @@ struct AlpnTests { } @Test func validAlpn() throws { - let alpn = try Alpn(version: "1.2", genesisHeader: "jamabcdefg") + var alpn = try Alpn(version: "1.2", genesisHeader: "jamabcdefg") #expect(alpn.alpnString == "jamnp-s/1.2/jama") } } From cfbda13b8496488c56eeacf41b3208c3ed9fde0d Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 24 Sep 2024 16:16:05 +0800 Subject: [PATCH 13/89] update alpn --- Networking/Sources/Networking/msquic/Alpn.swift | 11 ++--------- Networking/Sources/Networking/msquic/QuicStatus.swift | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Networking/Sources/Networking/msquic/Alpn.swift b/Networking/Sources/Networking/msquic/Alpn.swift index 720a706b..f3fcee55 100644 --- a/Networking/Sources/Networking/msquic/Alpn.swift +++ b/Networking/Sources/Networking/msquic/Alpn.swift @@ -1,19 +1,12 @@ import Foundation struct Alpn { - private let protocolName: String - private let version: String - private let genesisHeader: String + public let alpnString: String private static let headerPrefixLength = 4 - lazy var alpnString: String = "\(protocolName)/\(version)/\(genesisHeader.prefix(Alpn.headerPrefixLength))" - init(_ protocolName: String = "jamnp-s", version: String = "0", genesisHeader: String) throws { - self.protocolName = protocolName - self.version = version - guard genesisHeader.count >= Alpn.headerPrefixLength else { throw QuicError.invalidAlpn } - self.genesisHeader = genesisHeader + alpnString = "\(protocolName)/\(version)/\(genesisHeader.prefix(Alpn.headerPrefixLength))" } } diff --git a/Networking/Sources/Networking/msquic/QuicStatus.swift b/Networking/Sources/Networking/msquic/QuicStatus.swift index 93865881..1dcbe923 100644 --- a/Networking/Sources/Networking/msquic/QuicStatus.swift +++ b/Networking/Sources/Networking/msquic/QuicStatus.swift @@ -17,7 +17,7 @@ extension QuicStatus { } var isSucceeded: Bool { - self <= 0 + Int32(bitPattern: self) <= 0 } init(_ value: UInt32?) { From 724d7d1c75aa65596a1d885cc9d110cc9f0bb2d6 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 09:07:51 +0800 Subject: [PATCH 14/89] update quic client --- .../xcschemes/Networking.xcscheme | 79 +++++ .../xcschemes/NetworkingTests.xcscheme | 54 +++ Networking/Package.swift | 4 - .../Sources/Networking/msquic/NetAddr.swift | 17 + .../Networking/msquic/QuicClient.swift | 183 ++++++++++ .../Networking/msquic/QuicConfig.swift | 93 +++++ .../Networking/msquic/QuicConnection.swift | 257 ++++++++++++++ .../Networking/msquic/QuicStream.swift | 277 +++++++++++++++ .../Sources/Networking/msquic/Typealias.swift | 1 + .../Tests/NetworkingTests/AlpnTest.swift | 2 +- .../NetworkingTests/QuicClientTest.swift | 51 +++ Utils/Sources/Utils/AtomicArray.swift | 320 ++++++++++++++++++ Utils/Sources/Utils/AtomicDictionary.swift | 153 +++++++++ boka.xcodeproj/project.pbxproj | 2 + 14 files changed, 1488 insertions(+), 5 deletions(-) create mode 100644 Networking/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme create mode 100644 Networking/.swiftpm/xcode/xcshareddata/xcschemes/NetworkingTests.xcscheme create mode 100644 Networking/Sources/Networking/msquic/NetAddr.swift create mode 100644 Networking/Sources/Networking/msquic/QuicClient.swift create mode 100644 Networking/Sources/Networking/msquic/QuicConfig.swift create mode 100644 Networking/Sources/Networking/msquic/QuicConnection.swift create mode 100644 Networking/Sources/Networking/msquic/QuicStream.swift create mode 100644 Networking/Tests/NetworkingTests/QuicClientTest.swift create mode 100644 Utils/Sources/Utils/AtomicArray.swift create mode 100644 Utils/Sources/Utils/AtomicDictionary.swift diff --git a/Networking/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme b/Networking/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme new file mode 100644 index 00000000..943c06eb --- /dev/null +++ b/Networking/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Networking/.swiftpm/xcode/xcshareddata/xcschemes/NetworkingTests.xcscheme b/Networking/.swiftpm/xcode/xcshareddata/xcschemes/NetworkingTests.xcscheme new file mode 100644 index 00000000..d5063487 --- /dev/null +++ b/Networking/.swiftpm/xcode/xcshareddata/xcschemes/NetworkingTests.xcscheme @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Networking/Package.swift b/Networking/Package.swift index 6bc3f89a..704ab706 100644 --- a/Networking/Package.swift +++ b/Networking/Package.swift @@ -20,8 +20,6 @@ let package = Package( .package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"), .package(url: "https://github.com/apple/swift-testing.git", branch: "0.10.0"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"), - .package(url: "https://github.com/apple/swift-atomics.git", from: "1.2.0"), - ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. @@ -36,8 +34,6 @@ let package = Package( .product(name: "NIOCore", package: "swift-nio"), .product(name: "NIOPosix", package: "swift-nio"), .product(name: "Logging", package: "swift-log"), - .product(name: "Atomics", package: "swift-atomics"), - ], resources: [ .process("assets"), diff --git a/Networking/Sources/Networking/msquic/NetAddr.swift b/Networking/Sources/Networking/msquic/NetAddr.swift new file mode 100644 index 00000000..58de1692 --- /dev/null +++ b/Networking/Sources/Networking/msquic/NetAddr.swift @@ -0,0 +1,17 @@ +import Foundation + +struct NetAddr: Hashable { + var ipAddress: String + var port: UInt16 + + // Implement the hash(into:) method + func hash(into hasher: inout Hasher) { + hasher.combine(ipAddress) + hasher.combine(port) + } + + // Implement the == operator + static func == (lhs: NetAddr, rhs: NetAddr) -> Bool { + lhs.ipAddress == rhs.ipAddress && lhs.port == rhs.port + } +} diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift new file mode 100644 index 00000000..afc55e0b --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -0,0 +1,183 @@ +import Atomics +import Foundation +import Logging +import msquic +import NIO + +let clientLogger = Logger(label: "QuicClient") + +public protocol QuicClientMessageHandler: AnyObject { + func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) + // TODO: add error or remove it + func didReceiveError(quicClient: QuicClient, error: QuicError) +} + +public class QuicClient: @unchecked Sendable { + private var api: UnsafePointer? + private var registration: HQuic? + private var configuration: HQuic? + private var connection: QuicConnection? + private let config: QuicConfig + private weak var messageHandler: QuicClientMessageHandler? + private let isClosed: ManagedAtomic = .init(false) + + init(config: QuicConfig, messageHandler: QuicClientMessageHandler? = nil) throws { + self.config = config + self.messageHandler = messageHandler + var rawPointer: UnsafeRawPointer? + let status: UInt32 = MsQuicOpenVersion(2, &rawPointer) + + if QuicStatus(status).isFailed { + throw QuicError.invalidStatus(status: status.code) + } + guard + let boundPointer: UnsafePointer = rawPointer?.assumingMemoryBound( + to: QuicApiTable.self + ) + else { + throw QuicError.getApiFailed + } + + var registrationHandle: HQuic? + let registrationStatus = + boundPointer.pointee.RegistrationOpen(nil, ®istrationHandle) + if QuicStatus(registrationStatus).isFailed { + throw QuicError.invalidStatus(status: registrationStatus.code) + } + + api = boundPointer + registration = registrationHandle + } + + func start() throws -> QuicStatus { + let status = QuicStatusCode.success.rawValue + try loadConfiguration() + connection = QuicConnection( + api: api, registration: registration, configuration: configuration, messageHandler: self + ) + try connection?.open() + try connection?.start(ipAddress: config.ipAddress, port: config.port) + return status + } + + // Asynchronous send method that waits for a QuicMessage reply + func send(message: Data) async throws -> QuicMessage { + try await send(message: message, streamKind: .uniquePersistent) + } + + // send method that returns a QuicStatus + func send(message: Data, streamKind: StreamKind) throws -> QuicStatus { + guard let connection else { + throw QuicError.getConnectionFailed + } + let sendStream: QuicStream + // Check if there is an existing stream of the same kind + if streamKind == .uniquePersistent { + // If there is, send the message to the existing stream + sendStream = try connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + // If there is not, create a new stream + sendStream = try connection.createCommonEphemeralStream() + // Start the stream + try sendStream.start() + } + return sendStream.send(buffer: message, kind: streamKind) + } + + // Asynchronous send method that waits for a QuicMessage reply + func send(message: Data, streamKind: StreamKind = .uniquePersistent) async throws -> QuicMessage { + guard let connection else { + throw QuicError.getConnectionFailed + } + let sendStream: QuicStream + // Check if there is an existing stream of the same kind + if streamKind == .uniquePersistent { + // If there is, send the message to the existing stream + sendStream = try connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + // If there is not, create a new stream + sendStream = try connection.createCommonEphemeralStream() + // Start the stream + try sendStream.start() + } + return try await sendStream.send(buffer: message) + } + + func getNetAddr() -> NetAddr { + NetAddr(ipAddress: config.ipAddress, port: config.port) + } + + func close() { + if isClosed.compareExchange(expected: false, desired: true, ordering: .acquiring).exchanged { + if let connection { + connection.close() + self.connection = nil + } + + if let configuration { + api?.pointee.ConfigurationClose(configuration) + self.configuration = nil + } + + if let registration { + api?.pointee.RegistrationClose(registration) + self.registration = nil + } + + if api != nil { + MsQuicClose(api) + api = nil + } + + if let messageHandler { + messageHandler.didReceiveMessage( + quicClient: self, message: QuicMessage(type: .close, data: nil) + ) + } + clientLogger.info("QuicClient Close") + } + } + + deinit { + close() + clientLogger.info("QuicClient Deinit") + } +} + +extension QuicClient: QuicConnectionMessageHandler { + public func didReceiveMessage( + connection _: QuicConnection, stream _: QuicStream?, message: QuicMessage + ) { + switch message.type { + case .received: + let buffer = message.data! + clientLogger.info( + "Client received: \(String([UInt8](buffer).map { Character(UnicodeScalar($0)) }))" + ) + + case .shutdownComplete: + // Use [weak self] to avoid strong reference cycle + Task { [weak self] in + guard let self else { return } + close() + } + + default: + break + } + } + + public func didReceiveError( + connection _: QuicConnection, stream _: QuicStream, error: QuicError + ) { + clientLogger.error("Failed to receive message: \(error)") + } +} + +extension QuicClient { + private func loadConfiguration() throws { + configuration = try config.loadConfiguration( + api: api, registration: registration + ) + } +} diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift new file mode 100644 index 00000000..e0196b94 --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -0,0 +1,93 @@ +import Foundation +import msquic + +public struct QuicConfig { + public let id: String + public let cert: String + public let key: String + public let alpn: String + public let ipAddress: String + public let port: UInt16 + + public func loadConfiguration( + api: UnsafePointer?, + registration: HQuic? + ) throws -> HQuic? { + // Initialize QUIC settings + var settings = QuicSettings() + settings.IdleTimeoutMs = 10000 + settings.IsSet.IdleTimeoutMs = 1 + settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT + settings.IsSet.ServerResumptionLevel = 1 + settings.PeerBidiStreamCount = 1 + settings.IsSet.PeerBidiStreamCount = 1 + + // Initialize certificate and credential configurations + var certificateFile = QuicCertificateFile() + var credConfig = QuicCredentialConfig() + + memset(&certificateFile, 0, MemoryLayout.size(ofValue: certificateFile)) + memset(&credConfig, 0, MemoryLayout.size(ofValue: credConfig)) + + // Convert certificate and key paths to C strings + let certCString = cert.utf8CString + let keyFileCString = key.utf8CString + + // Allocate memory for certificate and key paths + let certPointer = UnsafeMutablePointer.allocate(capacity: certCString.count) + let keyFilePointer = UnsafeMutablePointer.allocate(capacity: keyFileCString.count) + + // Copy the C strings to the allocated memory + let certBufferPointer = UnsafeMutableBufferPointer(start: certPointer, count: certCString.count) + _ = certBufferPointer.initialize(from: certCString) + + let keyFileBufferPointer = UnsafeMutableBufferPointer(start: keyFilePointer, count: keyFileCString.count) + _ = keyFileBufferPointer.initialize(from: keyFileCString) + + // Set certificate file paths in QUIC_CERTIFICATE_FILE + certificateFile.CertificateFile = UnsafePointer(certPointer) + certificateFile.PrivateKeyFile = UnsafePointer(keyFilePointer) + + let certificateFilePointer = UnsafeMutablePointer.allocate(capacity: 1) + certificateFilePointer.initialize(to: certificateFile) + + // Configure credentials + credConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE + credConfig.Flags = QUIC_CREDENTIAL_FLAG_NONE + credConfig.CertificateFile = certificateFilePointer + + // Convert ALPN to data buffer + let buffer = Data(alpn.utf8) + let bufferPointer = UnsafeMutablePointer.allocate(capacity: buffer.count) + buffer.copyBytes(to: bufferPointer, count: buffer.count) + + // Ensure memory is deallocated + defer { + certPointer.deallocate() + keyFilePointer.deallocate() + certificateFilePointer.deallocate() + bufferPointer.deallocate() + } + + var alpn = QuicBuffer(Length: UInt32(buffer.count), Buffer: bufferPointer) + + // Open QUIC configuration + var configuration: HQuic? + let status = (api?.pointee.ConfigurationOpen( + registration, &alpn, 1, &settings, UInt32(MemoryLayout.size(ofValue: settings)), + nil, &configuration + )).status + + if status.isFailed { + throw QuicError.invalidStatus(status: status.code) + } + + // Load credentials into the configuration + let configStatus = (api?.pointee.ConfigurationLoadCredential(configuration, &credConfig)).status + if configStatus.isFailed { + throw QuicError.invalidStatus(status: configStatus.code) + } + + return configuration + } +} diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift new file mode 100644 index 00000000..5cee856b --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -0,0 +1,257 @@ +import Atomics +import Foundation +import Logging +import msquic +import Utils + +let logger = Logger(label: "QuicConnection") + +public protocol QuicConnectionMessageHandler: AnyObject { + func didReceiveMessage( + connection: QuicConnection, stream: QuicStream?, message: QuicMessage + ) + func didReceiveError( + connection: QuicConnection, stream: QuicStream, error: QuicError + ) +} + +public class QuicConnection { + private var connection: HQuic? + private var api: UnsafePointer? + private var registration: HQuic? + private var configuration: HQuic? + private var uniquePersistentStreams: AtomicDictionary + private var commonEphemeralStreams: AtomicArray + private weak var messageHandler: QuicConnectionMessageHandler? + private var connectionCallback: ConnectionCallback? + private let isClosed: ManagedAtomic = .init(false) + + // Initializer for creating a new connection + init( + api: UnsafePointer?, + registration: HQuic?, + configuration: HQuic?, + messageHandler: QuicConnectionMessageHandler? = nil + ) { + self.api = api + self.registration = registration + self.configuration = configuration + self.messageHandler = messageHandler + uniquePersistentStreams = .init() + commonEphemeralStreams = .init() + connectionCallback = { connection, context, event in + QuicConnection.connectionCallback( + connection: connection, context: context, event: event + ) + } + } + + // Initializer for wrapping an existing connection + init( + api: UnsafePointer?, registration: HQuic?, configuration: HQuic?, + connection: HQuic?, messageHandler: QuicConnectionMessageHandler? = nil + ) { + self.api = api + self.registration = registration + self.configuration = configuration + self.connection = connection + self.messageHandler = messageHandler + uniquePersistentStreams = .init() + commonEphemeralStreams = .init() + connectionCallback = { connection, context, event in + QuicConnection.connectionCallback( + connection: connection, context: context, event: event + ) + } + } + + // Sets the callback handler for the connection + func setCallbackHandler() -> QuicStatus { + guard let api, let connection, let configuration else { + return QuicStatusCode.invalidParameter.rawValue + } + + let callbackPointer = unsafeBitCast( + connectionCallback, to: UnsafeMutableRawPointer?.self + ) + + api.pointee.SetCallbackHandler( + connection, + callbackPointer, + UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + ) + + return api.pointee.ConnectionSetConfiguration(connection, configuration) + } + + // Opens the connection + func open() throws { + let status = + (api?.pointee.ConnectionOpen( + registration, + { connection, context, event -> QuicStatus in + return QuicConnection.connectionCallback( + connection: connection, context: context, event: event + ) + }, Unmanaged.passUnretained(self).toOpaque(), &connection + )).status + if status.isFailed { + throw QuicError.invalidStatus(status: status.code) + } + } + + // Creates or retrieves a unique persistent stream + func createOrGetUniquePersistentStream(kind: StreamKind) throws -> QuicStream { + if let stream = uniquePersistentStreams[kind] { + return stream + } + let stream = try QuicStream(api: api, connection: connection, kind, messageHandler: self) + uniquePersistentStreams[kind] = stream + try stream.start() + return stream + } + + // Creates a common ephemeral stream + func createCommonEphemeralStream() throws -> QuicStream { + let stream = try QuicStream(api: api, connection: connection, .commonEphemeral, messageHandler: self) + commonEphemeralStreams.append(stream) + return stream + } + + // Removes a stream from the connection + func removeStream(stream: QuicStream) { + stream.close() + if stream.kind == .uniquePersistent { + _ = uniquePersistentStreams.removeValue(forKey: stream.kind) + } else { + commonEphemeralStreams.removeAll(where: { $0 === stream }) + } + } + + // Starts the connection with the specified IP address and port + func start(ipAddress: String, port: UInt16) throws { + let status = + (api?.pointee.ConnectionStart( + connection, configuration, QUIC_ADDRESS_FAMILY(QUIC_ADDRESS_FAMILY_UNSPEC), + ipAddress, port + )).status + if status.isFailed { + throw QuicError.invalidStatus(status: status.code) + } + } + + // Closes the connection and cleans up resources + func close() { + if isClosed.compareExchange(expected: false, desired: true, ordering: .acquiring).exchanged { + connectionCallback = nil + messageHandler = nil + for stream in commonEphemeralStreams { + stream.close() + } + commonEphemeralStreams.removeAll() + for stream in uniquePersistentStreams.values { + stream.close() + } + uniquePersistentStreams.removeAll() + if connection != nil { + api?.pointee.ConnectionClose(connection) + connection = nil + } + logger.info("QuicConnection close") + } + } + + // Deinitializer to ensure resources are cleaned up + deinit { + close() + logger.info("QuicConnection Deinit") + } +} + +extension QuicConnection { + // Static callback function for handling connection events + private static func connectionCallback( + connection: HQuic?, context: UnsafeMutableRawPointer?, + event: UnsafePointer? + ) -> QuicStatus { + guard let context, let event else { + return QuicStatusCode.notSupported.rawValue + } + + let quicConnection: QuicConnection = Unmanaged.fromOpaque(context) + .takeUnretainedValue() + let status: QuicStatus = QuicStatusCode.success.rawValue + switch event.pointee.Type { + case QUIC_CONNECTION_EVENT_CONNECTED: + logger.info("[\(String(describing: connection))] Connected") + + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT: + if event.pointee.SHUTDOWN_INITIATED_BY_TRANSPORT.Status + == QuicStatusCode.connectionIdle.rawValue + { + logger.info( + "[\(String(describing: connection))] Successfully shut down on idle." + ) + } else { + logger.warning( + " Shut down by transport, 0x\(String(format: "%x", event.pointee.SHUTDOWN_INITIATED_BY_TRANSPORT.Status))" + ) + } + + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: + logger.warning( + " Shut down by peer, 0x\(String(format: "%llx", event.pointee.SHUTDOWN_INITIATED_BY_PEER.ErrorCode))" + ) + + case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: + logger.info("[\(String(describing: connection))] Shutdown all done") + if event.pointee.SHUTDOWN_COMPLETE.AppCloseInProgress == 0 { + if let messageHandler = quicConnection.messageHandler { + messageHandler.didReceiveMessage( + connection: quicConnection, + stream: nil, + message: QuicMessage(type: .shutdownComplete, data: nil) + ) + quicConnection.messageHandler = nil + } + } + + case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED: + logger.info( + " Ticket received (\(event.pointee.RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength) bytes)" + ) + + case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED: + logger.info("[\(String(describing: connection))] Peer stream started") + let stream = event.pointee.PEER_STREAM_STARTED.Stream + let quicStream = QuicStream( + api: quicConnection.api, connection: connection, stream: stream, messageHandler: quicConnection + ) + quicStream.setCallbackHandler() + quicConnection.commonEphemeralStreams.append(quicStream) + + default: + break + } + return status + } +} + +extension QuicConnection: QuicStreamMessageHandler { + // Handles received messages from the stream + public func didReceiveMessage(_ stream: QuicStream, message: QuicMessage) { + switch message.type { + case .shutdownComplete: + removeStream(stream: stream) + default: + break + } + messageHandler?.didReceiveMessage(connection: self, stream: stream, message: message) + } + + // Handles errors received from the stream + public func didReceiveError(_ stream: QuicStream, error: QuicError) { + logger.error("Failed to receive message: \(error)") + messageHandler?.didReceiveError(connection: self, stream: stream, error: error) + } +} diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift new file mode 100644 index 00000000..7f688966 --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -0,0 +1,277 @@ +import Atomics +import Foundation +import Logging +import msquic + +let streamLogger = Logger(label: "QuicStream") + +public enum StreamKind { + case uniquePersistent + case commonEphemeral + case unknown +} + +public protocol QuicStreamMessageHandler: AnyObject { + func didReceiveMessage(_ stream: QuicStream, message: QuicMessage) + func didReceiveError(_ stream: QuicStream, error: QuicError) +} + +public class QuicStream { + private var stream: HQuic? + private let api: UnsafePointer? + private let connection: HQuic? + public let kind: StreamKind + private weak var messageHandler: QuicStreamMessageHandler? + private var streamCallback: StreamCallback? + private var sendCompletion: CheckedContinuation? + private let isClosed: ManagedAtomic = .init(false) + + // Initializer for creating a new stream + init( + api: UnsafePointer?, connection: HQuic?, + _ streamKind: StreamKind = .uniquePersistent, + messageHandler: QuicStreamMessageHandler? = nil + ) throws { + self.api = api + self.connection = connection + self.messageHandler = messageHandler + kind = streamKind + streamCallback = { stream, context, event in + QuicStream.streamCallback( + stream: stream, context: context, event: event + ) + } + } + + // Initializer for wrapping an existing stream + init( + api: UnsafePointer?, connection: HQuic?, stream: HQuic?, + messageHandler: QuicStreamMessageHandler? = nil + ) { + self.api = api + self.connection = connection + self.messageHandler = messageHandler + self.stream = stream + kind = .commonEphemeral + streamCallback = { stream, context, event in + QuicStream.streamCallback( + stream: stream, context: context, event: event + ) + } + } + + // Opens a stream with the specified kind + private func openStream(_: StreamKind = .commonEphemeral) throws { + let status = + (api?.pointee.StreamOpen( + connection, QUIC_STREAM_OPEN_FLAG_NONE, + { stream, context, event -> QuicStatus in + QuicStream.streamCallback(stream: stream, context: context, event: event) + }, Unmanaged.passUnretained(self).toOpaque(), &stream + )).status + if status.isFailed { + throw QuicError.invalidStatus(status: status.code) + } + streamLogger.info("[\(String(describing: stream))] Stream opened") + } + + // Starts the stream + func start() throws { + try openStream(kind) + let status = (api?.pointee.StreamStart(stream, QUIC_STREAM_START_FLAG_NONE)).status + if status.isFailed { + throw QuicError.invalidStatus(status: status.code) + } + streamLogger.info("[\(String(describing: stream))] Stream started") + } + + // Closes the stream and cleans up resources + func close() { + if isClosed.compareExchange(expected: false, desired: true, ordering: .acquiring).exchanged { + streamLogger.info("QuicStream close") + streamCallback = nil + messageHandler = nil + if stream != nil { + api?.pointee.StreamClose(stream) + stream = nil + } + } + } + + // Sets the callback handler for the stream + func setCallbackHandler() { + guard let api, let stream else { + return + } + + let callbackPointer = unsafeBitCast( + streamCallback, to: UnsafeMutableRawPointer?.self + ) + + api.pointee.SetCallbackHandler( + stream, + callbackPointer, + UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + ) + } + + // Sends data over the stream and returns the status + func send(buffer: Data, kind: StreamKind? = nil) -> QuicStatus { + streamLogger.info("[\(String(describing: stream))] Sending data...") + var status = QuicStatusCode.success.rawValue + let messageLength = buffer.count + + let sendBufferRaw = UnsafeMutableRawPointer.allocate( + byteCount: MemoryLayout.size + messageLength, + alignment: MemoryLayout.alignment + ) + + let sendBuffer = sendBufferRaw.assumingMemoryBound(to: QUIC_BUFFER.self) + let bufferPointer = UnsafeMutablePointer.allocate( + capacity: messageLength + ) + buffer.copyBytes(to: bufferPointer, count: messageLength) + + sendBuffer.pointee.Buffer = bufferPointer + sendBuffer.pointee.Length = UInt32(messageLength) + + // Use the provided kind if available, otherwise use the stream's kind + let effectiveKind = kind ?? self.kind + let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN + + status = (api?.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw)).status + if status.isFailed { + streamLogger.error("StreamSend failed, \(status)!") + let shutdown: QuicStatus = + (api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)).status + if shutdown.isFailed { + streamLogger.error("StreamShutdown failed, 0x\(String(format: "%x", shutdown))!") + } + } + return status + } + + // Sends data over the stream asynchronously and waits for the response + func send(buffer: Data, kind: StreamKind? = nil) async throws -> QuicMessage { + streamLogger.info("[\(String(describing: stream))] Sending data...") + var status = QuicStatusCode.success.rawValue + let messageLength = buffer.count + + let sendBufferRaw = UnsafeMutableRawPointer.allocate( + byteCount: MemoryLayout.size + messageLength, + alignment: MemoryLayout.alignment + ) + + let sendBuffer = sendBufferRaw.assumingMemoryBound(to: QUIC_BUFFER.self) + let bufferPointer = UnsafeMutablePointer.allocate( + capacity: messageLength + ) + buffer.copyBytes(to: bufferPointer, count: messageLength) + + sendBuffer.pointee.Buffer = bufferPointer + sendBuffer.pointee.Length = UInt32(messageLength) + + // Use the provided kind if available, otherwise use the stream's kind + let effectiveKind = kind ?? self.kind + let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN + + return try await withCheckedThrowingContinuation { [weak self] continuation in + guard let self else { + continuation.resume(throwing: QuicError.sendFailed) + return + } + sendCompletion = continuation + status = (api?.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw)).status + if status.isFailed { + streamLogger.error("StreamSend failed, \(status)!") + let shutdown: QuicStatus = + (api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)).status + if shutdown.isFailed { + streamLogger.error( + "StreamShutdown failed, 0x\(String(format: "%x", shutdown))!" + ) + } + continuation.resume(throwing: QuicError.invalidStatus(status: status.code)) + sendCompletion = nil + } + } + } + + // Deinitializer to ensure resources are cleaned up + deinit { + close() + streamLogger.info("QuicStream Deinit") + } +} + +extension QuicStream { + // Static callback function for handling stream events + private static func streamCallback( + stream: HQuic?, context: UnsafeMutableRawPointer?, event: UnsafePointer? + ) -> QuicStatus { + guard let context, let event else { + return QuicStatusCode.notSupported.rawValue + } + + let quicStream: QuicStream = Unmanaged.fromOpaque(context).takeUnretainedValue() + var status: QuicStatus = QuicStatusCode.success.rawValue + streamLogger.info("[\(String(describing: stream))] Event: \(event.pointee.Type.rawValue)") + switch event.pointee.Type { + case QUIC_STREAM_EVENT_SEND_COMPLETE: + if let clientContext = event.pointee.SEND_COMPLETE.ClientContext { + free(clientContext) + } + streamLogger.info("[\(String(describing: stream))] Data sent") + + case QUIC_STREAM_EVENT_RECEIVE: + let bufferCount: UInt32 = event.pointee.RECEIVE.BufferCount + let buffers = event.pointee.RECEIVE.Buffers + var receivedData = Data() + for i in 0 ..< bufferCount { + let buffer = buffers![Int(i)] + let bufferLength = Int(buffer.Length) + let bufferData = Data(bytes: buffer.Buffer, count: bufferLength) + streamLogger.info( + " Data length \(bufferLength) bytes: \(String([UInt8](bufferData).map { Character(UnicodeScalar($0)) }))" + ) + receivedData.append(bufferData) + } + if receivedData.count > 0 { + if let continuation = quicStream.sendCompletion { + continuation.resume(returning: QuicMessage(type: .received, data: receivedData)) + quicStream.sendCompletion = nil + } + quicStream.messageHandler?.didReceiveMessage( + quicStream, message: QuicMessage(type: .received, data: receivedData) + ) + } + + case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: + streamLogger.info("[\(String(describing: stream))] Peer shut down") + + case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: + streamLogger.warning("[\(String(describing: stream))] Peer aborted") + status = + (quicStream.api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)) + .status + quicStream.messageHandler?.didReceiveError( + quicStream, error: QuicError.invalidStatus(status: status.code) + ) + + case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: + streamLogger.info("[\(String(describing: stream))] All done") + if event.pointee.SHUTDOWN_COMPLETE.AppCloseInProgress == 0 { + quicStream.api?.pointee.StreamClose(stream) + } + if let continuation = quicStream.sendCompletion { + continuation.resume(throwing: QuicError.sendFailed) + quicStream.sendCompletion = nil + } + + default: + break + } + + return status + } +} diff --git a/Networking/Sources/Networking/msquic/Typealias.swift b/Networking/Sources/Networking/msquic/Typealias.swift index bb2c66b7..0860b5a8 100644 --- a/Networking/Sources/Networking/msquic/Typealias.swift +++ b/Networking/Sources/Networking/msquic/Typealias.swift @@ -6,6 +6,7 @@ public typealias HQuic = HQUIC public typealias QuicBuffer = QUIC_BUFFER public typealias QuicSettings = QUIC_SETTINGS public typealias QuicCredentialConfig = QUIC_CREDENTIAL_CONFIG +public typealias QuicCertificateFile = QUIC_CERTIFICATE_FILE public typealias QuicListenerEvent = QUIC_LISTENER_EVENT public typealias QuicConnectionEvent = QUIC_CONNECTION_EVENT public typealias QuicStreamEvent = QUIC_STREAM_EVENT diff --git a/Networking/Tests/NetworkingTests/AlpnTest.swift b/Networking/Tests/NetworkingTests/AlpnTest.swift index abb083c3..419b0dba 100644 --- a/Networking/Tests/NetworkingTests/AlpnTest.swift +++ b/Networking/Tests/NetworkingTests/AlpnTest.swift @@ -11,7 +11,7 @@ struct AlpnTests { } @Test func validAlpn() throws { - var alpn = try Alpn(version: "1.2", genesisHeader: "jamabcdefg") + let alpn = try Alpn(version: "1.2", genesisHeader: "jamabcdefg") #expect(alpn.alpnString == "jamnp-s/1.2/jama") } } diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift new file mode 100644 index 00000000..cb8bea72 --- /dev/null +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -0,0 +1,51 @@ +import Foundation +import NIO +import Testing + +@testable import Networking + +#if os(macOS) + import CoreFoundation + import Security + + final class QuicClientTests { + @Test func start() async throws { + do { + let cert = Bundle.module.path(forResource: "server", ofType: "cert")! + let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let quicClient = try QuicClient( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4569 + ) + ) + let status = try quicClient.start() + print(status) + let message1 = try await quicClient.send( + message: Data("Hello, World!".utf8), streamKind: .uniquePersistent + ) + print("Client received 1: \(message1)") + let message2 = try await quicClient.send( + message: Data("Hello, swift!".utf8), streamKind: .commonEphemeral + ) + print("Client received 2: \(message2)") + let message3 = try await quicClient.send( + message: Data("Hello, how are you!".utf8), streamKind: .uniquePersistent + ) + print("Client received 3: \(message3)") + let message4 = try await quicClient.send( + message: Data("Hello, i am fine!".utf8), streamKind: .commonEphemeral + ) + print("Client received 4: \(message4)") + + try await group.next().scheduleTask(in: .seconds(5)) { + print("scheduleTask: 5s") + }.futureResult.get() + } catch { + // Handle the error if sending the message fails or if the connection fails + print("Failed about quic client: \(error)") + } + } + } +#endif diff --git a/Utils/Sources/Utils/AtomicArray.swift b/Utils/Sources/Utils/AtomicArray.swift new file mode 100644 index 00000000..d3cea7a7 --- /dev/null +++ b/Utils/Sources/Utils/AtomicArray.swift @@ -0,0 +1,320 @@ +import Foundation + +public struct AtomicArray: RangeReplaceableCollection { + public typealias Element = T + public typealias Index = Int + public typealias SubSequence = AtomicArray + public typealias Indices = Range + + fileprivate var array: [T] + private let queue = DispatchQueue(label: "com.atomicArray.queue", attributes: .concurrent) + + public var startIndex: Int { array.startIndex } + public var endIndex: Int { array.endIndex } + public var indices: Range { array.indices } + + public init() { + array = [] + } + + public init(_ elements: S) where S: Sequence, AtomicArray.Element == S.Element { + array = Array(elements) + } + + public init(repeating repeatedValue: AtomicArray.Element, count: Int) { + array = Array(repeating: repeatedValue, count: count) + } + + public func index(after i: Int) -> Int { + array.index(after: i) + } + + private func _read(_ block: () throws -> R) rethrows -> R { + var result: R! + try queue.sync { + result = try block() + } + return result + } + + private func _write(_ block: () throws -> R) rethrows -> R { + var result: R! + try queue.sync(flags: .barrier) { + result = try block() + } + return result + } + + public mutating func append(_ newElement: AtomicArray.Element) { + _write { + array.append(newElement) + } + } + + public mutating func append(contentsOf newElements: S) where S: Sequence, AtomicArray.Element == S.Element { + _write { + array.append(contentsOf: newElements) + } + } + + func filter(_ isIncluded: (AtomicArray.Element) throws -> Bool) rethrows -> AtomicArray { + try _read { + let subArray = try array.filter(isIncluded) + return AtomicArray(subArray) + } + } + + public mutating func insert(_ newElement: AtomicArray.Element, at i: AtomicArray.Index) { + _write { + array.insert(newElement, at: i) + } + } + + public mutating func insert(contentsOf newElements: S, at i: AtomicArray.Index) where S: Collection, + AtomicArray.Element == S.Element + { + _write { + array.insert(contentsOf: newElements, at: i) + } + } + + @discardableResult mutating func popLast() -> AtomicArray.Element? { + _write { + array.popLast() + } + } + + @discardableResult public mutating func remove(at i: AtomicArray.Index) -> AtomicArray.Element { + _write { + array.remove(at: i) + } + } + + mutating func removeAll() { + _write { + array.removeAll() + } + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool) { + _write { + array.removeAll(keepingCapacity: keepCapacity) + } + } + + public mutating func removeAll(where shouldBeRemoved: (AtomicArray.Element) throws -> Bool) rethrows { + try _write { + try array.removeAll(where: shouldBeRemoved) + } + } + + @discardableResult public mutating func removeFirst() -> AtomicArray.Element { + _write { + array.removeFirst() + } + } + + public mutating func removeFirst(_ k: Int) { + _write { + array.removeFirst(k) + } + } + + @discardableResult mutating func removeLast() -> AtomicArray.Element { + _write { + array.removeLast() + } + } + + mutating func removeLast(_ k: Int) { + _write { + array.removeLast(k) + } + } + + public func forEach(_ body: (Element) throws -> Void) rethrows { + try _read { + try array.forEach(body) + } + } + + mutating func removeFirstIfExist(where shouldBeRemoved: (AtomicArray.Element) throws -> Bool) { + _write { + guard let index = try? array.firstIndex(where: shouldBeRemoved) else { return } + array.remove(at: index) + } + } + + public mutating func removeSubrange(_ bounds: Range) { + _write { + array.removeSubrange(bounds) + } + } + + public mutating func replaceSubrange(_ subrange: R, with newElements: C) where C: Collection, R: RangeExpression, T == C.Element, + AtomicArray.Index == R.Bound + { + _write { + array.replaceSubrange(subrange, with: newElements) + } + } + + public mutating func reserveCapacity(_ n: Int) { + _write { + array.reserveCapacity(n) + } + } + + public var count: Int { + _read { + array.count + } + } + + public var isEmpty: Bool { + _read { + array.isEmpty + } + } + + public var first: AtomicArray.Element? { + _read { + array.first + } + } + + // Single action + + func getArray() -> [T] { + _read { + array + } + } + + mutating func setArray(_ newArray: [T]) { + _write { + array = newArray + } + } + + // Multi actions + + mutating func performRead(_ closure: ([T]) -> Void) { + _read { + closure(array) + } + } + + mutating func performWrite(_ closure: ([T]) -> ([T])) { + _write { + array = closure(array) + } + } + + public subscript(bounds: Range) -> AtomicArray.SubSequence { + _read { + AtomicArray(array[bounds]) + } + } + + public subscript(bounds: AtomicArray.Index) -> AtomicArray.Element { + get { + _read { + array[bounds] + } + } + set(value) { + _write { + array[bounds] = value + } + } + } + + static func + (lhs: Other, rhs: AtomicArray) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { + AtomicArray(lhs + rhs.getArray()) + } + + static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { + AtomicArray(lhs.getArray() + rhs) + } + + static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: RangeReplaceableCollection, + AtomicArray.Element == Other.Element + { + AtomicArray(lhs.getArray() + rhs) + } + + static func + (lhs: AtomicArray, rhs: AtomicArray) -> AtomicArray { + AtomicArray(lhs.getArray() + rhs.getArray()) + } + + static func += (lhs: inout AtomicArray, rhs: Other) where Other: Sequence, AtomicArray.Element == Other.Element { + lhs._write { + lhs.array += rhs + } + } +} + +extension AtomicArray: CustomStringConvertible { + public var description: String { + _read { + "\(array)" + } + } +} + +extension AtomicArray where Element: Equatable { + func split(separator: Element, maxSplits: Int, omittingEmptySubsequences: Bool) -> [ArraySlice] { + _read { + array.split(separator: separator, maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences) + } + } + + func firstIndex(of element: Element) -> Int? { + _read { + array.firstIndex(of: element) + } + } + + func lastIndex(of element: Element) -> Int? { + _read { + array.lastIndex(of: element) + } + } + + func starts(with possiblePrefix: PossiblePrefix) -> Bool where PossiblePrefix: Sequence, + Element == PossiblePrefix.Element + { + _read { + array.starts(with: possiblePrefix) + } + } + + func elementsEqual(_ other: OtherSequence) -> Bool where OtherSequence: Sequence, Element == OtherSequence.Element { + _read { + array.elementsEqual(other) + } + } + + func contains(_ element: Element) -> Bool { + _read { + array.contains(element) + } + } + + static func != (lhs: AtomicArray, rhs: AtomicArray) -> Bool { + lhs._read { + rhs._read { + lhs.array != rhs.array + } + } + } + + static func == (lhs: AtomicArray, rhs: AtomicArray) -> Bool { + lhs._read { + rhs._read { + lhs.array == rhs.array + } + } + } +} diff --git a/Utils/Sources/Utils/AtomicDictionary.swift b/Utils/Sources/Utils/AtomicDictionary.swift new file mode 100644 index 00000000..ffd6d7c2 --- /dev/null +++ b/Utils/Sources/Utils/AtomicDictionary.swift @@ -0,0 +1,153 @@ +import Foundation + +public struct AtomicDictionary { + private var dictionary: [Key: Value] + private let queue = DispatchQueue(label: "com.atomicDictionary.queue", attributes: .concurrent) + + public init() { + dictionary = [:] + } + + public init(_ elements: [Key: Value]) { + dictionary = elements + } + + private func _read(_ block: () throws -> R) rethrows -> R { + var result: R! + try queue.sync { + result = try block() + } + return result + } + + private func _write(_ block: () throws -> R) rethrows -> R { + var result: R! + try queue.sync(flags: .barrier) { + result = try block() + } + return result + } + + public subscript(key: Key) -> Value? { + get { + _read { + dictionary[key] + } + } + set { + _write { + dictionary[key] = newValue + } + } + } + + public mutating func set(value: Value, forKey key: Key) { + _write { + dictionary[key] = value + } + } + + public func value(forKey key: Key) -> Value? { + _read { + dictionary[key] + } + } + + public var count: Int { + _read { + dictionary.count + } + } + + public var isEmpty: Bool { + _read { + dictionary.isEmpty + } + } + + public var keys: [Key] { + _read { + Array(dictionary.keys) + } + } + + public var values: [Value] { + _read { + Array(dictionary.values) + } + } + + public func contains(key: Key) -> Bool { + _read { + dictionary.keys.contains(key) + } + } + + public mutating func removeValue(forKey key: Key) -> Value? { + _write { + dictionary.removeValue(forKey: key) + } + } + + public mutating func removeAll() { + _write { + dictionary.removeAll() + } + } + + public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { + _write { + dictionary.updateValue(value, forKey: key) + } + } + + public func forEach(_ body: ((key: Key, value: Value)) throws -> Void) rethrows { + try _read { + try dictionary.forEach(body) + } + } + + public func filter(_ isIncluded: ((key: Key, value: Value)) throws -> Bool) rethrows -> AtomicDictionary { + try _read { + let filtered = try dictionary.filter(isIncluded) + return AtomicDictionary(filtered) + } + } + + public mutating func merge(_ other: [Key: Value], uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows { + try _write { + try dictionary.merge(other, uniquingKeysWith: combine) + } + } + + public mutating func merge(_ other: AtomicDictionary, uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows { + try _write { + try dictionary.merge(other.dictionary, uniquingKeysWith: combine) + } + } + + public func mapValues(_ transform: (Value) throws -> T) rethrows -> AtomicDictionary { + try _read { + let mapped = try dictionary.mapValues(transform) + return AtomicDictionary(mapped) + } + } + + public func compactMapValues(_ transform: (Value) throws -> T?) rethrows -> AtomicDictionary { + try _read { + let compactMapped = try dictionary.compactMapValues(transform) + return AtomicDictionary(compactMapped) + } + } +} + +// Equatable conformance +extension AtomicDictionary: Equatable where Value: Equatable { + public static func == (lhs: AtomicDictionary, rhs: AtomicDictionary) -> Bool { + lhs._read { + rhs._read { + lhs.dictionary == rhs.dictionary + } + } + } +} diff --git a/boka.xcodeproj/project.pbxproj b/boka.xcodeproj/project.pbxproj index 3b37a32a..7cb1346a 100644 --- a/boka.xcodeproj/project.pbxproj +++ b/boka.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 06E2B78F2C7304FF00E35A48 /* Codec */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Codec; sourceTree = ""; }; 06F2335F2C0B306000A5E2E0 /* Database */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Database; sourceTree = ""; }; 06F233602C0C69F100A5E2E0 /* Utils */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Utils; sourceTree = ""; }; + 5A48C0302CA2D8FA000927F7 /* Networking */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Networking; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -33,6 +34,7 @@ 066C926F2C095D76005DDE4F /* Blockchain */, 066C926E2C095D67005DDE4F /* Boka */, 063EC59D2C33954600BF1550 /* JAMTests */, + 5A48C0302CA2D8FA000927F7 /* Networking */, ); sourceTree = ""; }; From b292d81269a2aed93a8e51ca080c40aecd9171de Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 09:27:30 +0800 Subject: [PATCH 15/89] update atomic --- Utils/Sources/Utils/AtomicArray.swift | 65 ++++++++++++++------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/Utils/Sources/Utils/AtomicArray.swift b/Utils/Sources/Utils/AtomicArray.swift index d3cea7a7..3f11a6f4 100644 --- a/Utils/Sources/Utils/AtomicArray.swift +++ b/Utils/Sources/Utils/AtomicArray.swift @@ -29,7 +29,7 @@ public struct AtomicArray: RangeReplaceableCollection { array.index(after: i) } - private func _read(_ block: () throws -> R) rethrows -> R { + fileprivate func _read(_ block: () throws -> R) rethrows -> R { var result: R! try queue.sync { result = try block() @@ -37,7 +37,7 @@ public struct AtomicArray: RangeReplaceableCollection { return result } - private func _write(_ block: () throws -> R) rethrows -> R { + fileprivate func _write(_ block: () throws -> R) rethrows -> R { var result: R! try queue.sync(flags: .barrier) { result = try block() @@ -57,7 +57,7 @@ public struct AtomicArray: RangeReplaceableCollection { } } - func filter(_ isIncluded: (AtomicArray.Element) throws -> Bool) rethrows -> AtomicArray { + public func filter(_ isIncluded: (AtomicArray.Element) throws -> Bool) rethrows -> AtomicArray { try _read { let subArray = try array.filter(isIncluded) return AtomicArray(subArray) @@ -78,19 +78,21 @@ public struct AtomicArray: RangeReplaceableCollection { } } - @discardableResult mutating func popLast() -> AtomicArray.Element? { + @discardableResult + public mutating func popLast() -> AtomicArray.Element? { _write { array.popLast() } } - @discardableResult public mutating func remove(at i: AtomicArray.Index) -> AtomicArray.Element { + @discardableResult + public mutating func remove(at i: AtomicArray.Index) -> AtomicArray.Element { _write { array.remove(at: i) } } - mutating func removeAll() { + public mutating func removeAll() { _write { array.removeAll() } @@ -108,7 +110,8 @@ public struct AtomicArray: RangeReplaceableCollection { } } - @discardableResult public mutating func removeFirst() -> AtomicArray.Element { + @discardableResult + public mutating func removeFirst() -> AtomicArray.Element { _write { array.removeFirst() } @@ -120,25 +123,27 @@ public struct AtomicArray: RangeReplaceableCollection { } } - @discardableResult mutating func removeLast() -> AtomicArray.Element { + @discardableResult + public mutating func removeLast() -> AtomicArray.Element { _write { array.removeLast() } } - mutating func removeLast(_ k: Int) { + public mutating func removeLast(_ k: Int) { _write { array.removeLast(k) } } + @inlinable public func forEach(_ body: (Element) throws -> Void) rethrows { try _read { try array.forEach(body) } } - mutating func removeFirstIfExist(where shouldBeRemoved: (AtomicArray.Element) throws -> Bool) { + public mutating func removeFirstIfExist(where shouldBeRemoved: (AtomicArray.Element) throws -> Bool) { _write { guard let index = try? array.firstIndex(where: shouldBeRemoved) else { return } array.remove(at: index) @@ -183,29 +188,25 @@ public struct AtomicArray: RangeReplaceableCollection { } } - // Single action - - func getArray() -> [T] { + public func getArray() -> [T] { _read { array } } - mutating func setArray(_ newArray: [T]) { + public mutating func setArray(_ newArray: [T]) { _write { array = newArray } } - // Multi actions - - mutating func performRead(_ closure: ([T]) -> Void) { + public mutating func performRead(_ closure: ([T]) -> Void) { _read { closure(array) } } - mutating func performWrite(_ closure: ([T]) -> ([T])) { + public mutating func performWrite(_ closure: ([T]) -> ([T])) { _write { array = closure(array) } @@ -230,25 +231,25 @@ public struct AtomicArray: RangeReplaceableCollection { } } - static func + (lhs: Other, rhs: AtomicArray) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { + public static func + (lhs: Other, rhs: AtomicArray) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { AtomicArray(lhs + rhs.getArray()) } - static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { + public static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { AtomicArray(lhs.getArray() + rhs) } - static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: RangeReplaceableCollection, + public static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: RangeReplaceableCollection, AtomicArray.Element == Other.Element { AtomicArray(lhs.getArray() + rhs) } - static func + (lhs: AtomicArray, rhs: AtomicArray) -> AtomicArray { + public static func + (lhs: AtomicArray, rhs: AtomicArray) -> AtomicArray { AtomicArray(lhs.getArray() + rhs.getArray()) } - static func += (lhs: inout AtomicArray, rhs: Other) where Other: Sequence, AtomicArray.Element == Other.Element { + public static func += (lhs: inout AtomicArray, rhs: Other) where Other: Sequence, AtomicArray.Element == Other.Element { lhs._write { lhs.array += rhs } @@ -264,25 +265,25 @@ extension AtomicArray: CustomStringConvertible { } extension AtomicArray where Element: Equatable { - func split(separator: Element, maxSplits: Int, omittingEmptySubsequences: Bool) -> [ArraySlice] { + public func split(separator: Element, maxSplits: Int, omittingEmptySubsequences: Bool) -> [ArraySlice] { _read { array.split(separator: separator, maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences) } } - func firstIndex(of element: Element) -> Int? { + public func firstIndex(of element: Element) -> Int? { _read { array.firstIndex(of: element) } } - func lastIndex(of element: Element) -> Int? { + public func lastIndex(of element: Element) -> Int? { _read { array.lastIndex(of: element) } } - func starts(with possiblePrefix: PossiblePrefix) -> Bool where PossiblePrefix: Sequence, + public func starts(with possiblePrefix: PossiblePrefix) -> Bool where PossiblePrefix: Sequence, Element == PossiblePrefix.Element { _read { @@ -290,19 +291,21 @@ extension AtomicArray where Element: Equatable { } } - func elementsEqual(_ other: OtherSequence) -> Bool where OtherSequence: Sequence, Element == OtherSequence.Element { + public func elementsEqual(_ other: OtherSequence) -> Bool where OtherSequence: Sequence, + Element == OtherSequence.Element + { _read { array.elementsEqual(other) } } - func contains(_ element: Element) -> Bool { + public func contains(_ element: Element) -> Bool { _read { array.contains(element) } } - static func != (lhs: AtomicArray, rhs: AtomicArray) -> Bool { + public static func != (lhs: AtomicArray, rhs: AtomicArray) -> Bool { lhs._read { rhs._read { lhs.array != rhs.array @@ -310,7 +313,7 @@ extension AtomicArray where Element: Equatable { } } - static func == (lhs: AtomicArray, rhs: AtomicArray) -> Bool { + public static func == (lhs: AtomicArray, rhs: AtomicArray) -> Bool { lhs._read { rhs._read { lhs.array == rhs.array From ae47b0aed1f5304126f5a5b1d5de62fd6878f182 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 09:32:07 +0800 Subject: [PATCH 16/89] update atomic array & dictionary --- Utils/Sources/Utils/AtomicArray.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Utils/Sources/Utils/AtomicArray.swift b/Utils/Sources/Utils/AtomicArray.swift index 3f11a6f4..91faf317 100644 --- a/Utils/Sources/Utils/AtomicArray.swift +++ b/Utils/Sources/Utils/AtomicArray.swift @@ -136,7 +136,6 @@ public struct AtomicArray: RangeReplaceableCollection { } } - @inlinable public func forEach(_ body: (Element) throws -> Void) rethrows { try _read { try array.forEach(body) From 9e9633204388914bde173b762d9294c7f5d11ed7 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 09:34:49 +0800 Subject: [PATCH 17/89] Update AtomicArray.swift --- Utils/Sources/Utils/AtomicArray.swift | 58 +++++++++++++-------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/Utils/Sources/Utils/AtomicArray.swift b/Utils/Sources/Utils/AtomicArray.swift index 91faf317..e6dff5ff 100644 --- a/Utils/Sources/Utils/AtomicArray.swift +++ b/Utils/Sources/Utils/AtomicArray.swift @@ -26,7 +26,7 @@ public struct AtomicArray: RangeReplaceableCollection { } public func index(after i: Int) -> Int { - array.index(after: i) + return array.index(after: i) } fileprivate func _read(_ block: () throws -> R) rethrows -> R { @@ -58,7 +58,7 @@ public struct AtomicArray: RangeReplaceableCollection { } public func filter(_ isIncluded: (AtomicArray.Element) throws -> Bool) rethrows -> AtomicArray { - try _read { + return try _read { let subArray = try array.filter(isIncluded) return AtomicArray(subArray) } @@ -70,9 +70,7 @@ public struct AtomicArray: RangeReplaceableCollection { } } - public mutating func insert(contentsOf newElements: S, at i: AtomicArray.Index) where S: Collection, - AtomicArray.Element == S.Element - { + public mutating func insert(contentsOf newElements: S, at i: AtomicArray.Index) where S: Collection, AtomicArray.Element == S.Element { _write { array.insert(contentsOf: newElements, at: i) } @@ -80,14 +78,14 @@ public struct AtomicArray: RangeReplaceableCollection { @discardableResult public mutating func popLast() -> AtomicArray.Element? { - _write { + return _write { array.popLast() } } @discardableResult public mutating func remove(at i: AtomicArray.Index) -> AtomicArray.Element { - _write { + return _write { array.remove(at: i) } } @@ -112,7 +110,7 @@ public struct AtomicArray: RangeReplaceableCollection { @discardableResult public mutating func removeFirst() -> AtomicArray.Element { - _write { + return _write { array.removeFirst() } } @@ -125,7 +123,7 @@ public struct AtomicArray: RangeReplaceableCollection { @discardableResult public mutating func removeLast() -> AtomicArray.Element { - _write { + return _write { array.removeLast() } } @@ -170,25 +168,25 @@ public struct AtomicArray: RangeReplaceableCollection { } public var count: Int { - _read { + return _read { array.count } } public var isEmpty: Bool { - _read { + return _read { array.isEmpty } } public var first: AtomicArray.Element? { - _read { + return _read { array.first } } public func getArray() -> [T] { - _read { + return _read { array } } @@ -212,14 +210,14 @@ public struct AtomicArray: RangeReplaceableCollection { } public subscript(bounds: Range) -> AtomicArray.SubSequence { - _read { + return _read { AtomicArray(array[bounds]) } } public subscript(bounds: AtomicArray.Index) -> AtomicArray.Element { get { - _read { + return _read { array[bounds] } } @@ -231,21 +229,21 @@ public struct AtomicArray: RangeReplaceableCollection { } public static func + (lhs: Other, rhs: AtomicArray) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { - AtomicArray(lhs + rhs.getArray()) + return AtomicArray(lhs + rhs.getArray()) } public static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element { - AtomicArray(lhs.getArray() + rhs) + return AtomicArray(lhs.getArray() + rhs) } public static func + (lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: RangeReplaceableCollection, AtomicArray.Element == Other.Element { - AtomicArray(lhs.getArray() + rhs) + return AtomicArray(lhs.getArray() + rhs) } public static func + (lhs: AtomicArray, rhs: AtomicArray) -> AtomicArray { - AtomicArray(lhs.getArray() + rhs.getArray()) + return AtomicArray(lhs.getArray() + rhs.getArray()) } public static func += (lhs: inout AtomicArray, rhs: Other) where Other: Sequence, AtomicArray.Element == Other.Element { @@ -257,7 +255,7 @@ public struct AtomicArray: RangeReplaceableCollection { extension AtomicArray: CustomStringConvertible { public var description: String { - _read { + return _read { "\(array)" } } @@ -265,19 +263,19 @@ extension AtomicArray: CustomStringConvertible { extension AtomicArray where Element: Equatable { public func split(separator: Element, maxSplits: Int, omittingEmptySubsequences: Bool) -> [ArraySlice] { - _read { + return _read { array.split(separator: separator, maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences) } } public func firstIndex(of element: Element) -> Int? { - _read { + return _read { array.firstIndex(of: element) } } public func lastIndex(of element: Element) -> Int? { - _read { + return _read { array.lastIndex(of: element) } } @@ -285,27 +283,25 @@ extension AtomicArray where Element: Equatable { public func starts(with possiblePrefix: PossiblePrefix) -> Bool where PossiblePrefix: Sequence, Element == PossiblePrefix.Element { - _read { + return _read { array.starts(with: possiblePrefix) } } - public func elementsEqual(_ other: OtherSequence) -> Bool where OtherSequence: Sequence, - Element == OtherSequence.Element - { - _read { + public func elementsEqual(_ other: OtherSequence) -> Bool where OtherSequence: Sequence, Element == OtherSequence.Element { + return _read { array.elementsEqual(other) } } public func contains(_ element: Element) -> Bool { - _read { + return _read { array.contains(element) } } public static func != (lhs: AtomicArray, rhs: AtomicArray) -> Bool { - lhs._read { + return lhs._read { rhs._read { lhs.array != rhs.array } @@ -313,7 +309,7 @@ extension AtomicArray where Element: Equatable { } public static func == (lhs: AtomicArray, rhs: AtomicArray) -> Bool { - lhs._read { + return lhs._read { rhs._read { lhs.array == rhs.array } From aa58e5f13a5a683c3d16b04c378f1f20d3c60cbc Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 09:35:15 +0800 Subject: [PATCH 18/89] Update AtomicDictionary.swift --- Utils/Sources/Utils/AtomicDictionary.swift | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Utils/Sources/Utils/AtomicDictionary.swift b/Utils/Sources/Utils/AtomicDictionary.swift index ffd6d7c2..c3e4c2d6 100644 --- a/Utils/Sources/Utils/AtomicDictionary.swift +++ b/Utils/Sources/Utils/AtomicDictionary.swift @@ -30,7 +30,7 @@ public struct AtomicDictionary { public subscript(key: Key) -> Value? { get { - _read { + return _read { dictionary[key] } } @@ -48,43 +48,43 @@ public struct AtomicDictionary { } public func value(forKey key: Key) -> Value? { - _read { + return _read { dictionary[key] } } public var count: Int { - _read { + return _read { dictionary.count } } public var isEmpty: Bool { - _read { + return _read { dictionary.isEmpty } } public var keys: [Key] { - _read { + return _read { Array(dictionary.keys) } } public var values: [Value] { - _read { + return _read { Array(dictionary.values) } } public func contains(key: Key) -> Bool { - _read { + return _read { dictionary.keys.contains(key) } } public mutating func removeValue(forKey key: Key) -> Value? { - _write { + return _write { dictionary.removeValue(forKey: key) } } @@ -96,7 +96,7 @@ public struct AtomicDictionary { } public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { - _write { + return _write { dictionary.updateValue(value, forKey: key) } } @@ -108,7 +108,7 @@ public struct AtomicDictionary { } public func filter(_ isIncluded: ((key: Key, value: Value)) throws -> Bool) rethrows -> AtomicDictionary { - try _read { + return try _read { let filtered = try dictionary.filter(isIncluded) return AtomicDictionary(filtered) } @@ -127,14 +127,14 @@ public struct AtomicDictionary { } public func mapValues(_ transform: (Value) throws -> T) rethrows -> AtomicDictionary { - try _read { + return try _read { let mapped = try dictionary.mapValues(transform) return AtomicDictionary(mapped) } } public func compactMapValues(_ transform: (Value) throws -> T?) rethrows -> AtomicDictionary { - try _read { + return try _read { let compactMapped = try dictionary.compactMapValues(transform) return AtomicDictionary(compactMapped) } @@ -144,7 +144,7 @@ public struct AtomicDictionary { // Equatable conformance extension AtomicDictionary: Equatable where Value: Equatable { public static func == (lhs: AtomicDictionary, rhs: AtomicDictionary) -> Bool { - lhs._read { + return lhs._read { rhs._read { lhs.dictionary == rhs.dictionary } From c672c6d4b7122de07fe1fe055522544ec3814fe3 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 10:08:15 +0800 Subject: [PATCH 19/89] update atomic test --- Utils/Tests/UtilsTests/AtomicArrayTests.swift | 43 ++++++ .../UtilsTests/AtomicDictionaryTests.swift | 145 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 Utils/Tests/UtilsTests/AtomicArrayTests.swift create mode 100644 Utils/Tests/UtilsTests/AtomicDictionaryTests.swift diff --git a/Utils/Tests/UtilsTests/AtomicArrayTests.swift b/Utils/Tests/UtilsTests/AtomicArrayTests.swift new file mode 100644 index 00000000..2124dc0b --- /dev/null +++ b/Utils/Tests/UtilsTests/AtomicArrayTests.swift @@ -0,0 +1,43 @@ +import Foundation +import Testing + +@testable import Utils + +struct AtomicArrayTests { + @Test func initArray() throws { + let array = AtomicArray(repeating: 1, count: 5) + #expect(array.getArray() == [1, 1, 1, 1, 1]) + #expect(array.count == 5) + } + + @Test func appendElement() throws { + var array = AtomicArray([1, 2, 3, 4, 5]) + array.append(6) + #expect(array.getArray() == [1, 2, 3, 4, 5, 6]) + #expect(array.count == 6) + } + + @Test func insertElement() throws { + var array = AtomicArray([1, 2, 3, 4, 5]) + array.insert(0, at: 2) + #expect(array.getArray() == [1, 2, 0, 3, 4, 5]) + #expect(array.count == 6) + } + + @Test func removeElement() throws { + var array = AtomicArray([1, 2, 3, 4, 5, 6]) + let removed = array.remove(at: 2) + #expect(removed == 3) + #expect(array.getArray() == [1, 2, 4, 5, 6]) + #expect(array.count == 5) + } + + @Test func equatable() throws { + let array1 = AtomicArray([1, 2, 3, 4, 5]) + let array2 = AtomicArray([1, 2, 3, 4, 5]) + let array3 = AtomicArray([5, 4, 3, 2, 1]) + + #expect(array1 == array2) + #expect(array1 != array3) + } +} diff --git a/Utils/Tests/UtilsTests/AtomicDictionaryTests.swift b/Utils/Tests/UtilsTests/AtomicDictionaryTests.swift new file mode 100644 index 00000000..0139d002 --- /dev/null +++ b/Utils/Tests/UtilsTests/AtomicDictionaryTests.swift @@ -0,0 +1,145 @@ +import Foundation +import Testing + +@testable import Utils + +struct AtomicDictionaryTests { + @Test func initDictionary() throws { + let dict = AtomicDictionary() + #expect(dict.count == 0) + #expect(dict.isEmpty) + } + + @Test func initWithElements() throws { + let elements = ["one": 1, "two": 2, "three": 3] + let dict = AtomicDictionary(elements) + #expect(dict.count == 3) + #expect(dict.value(forKey: "one") == 1) + #expect(dict.value(forKey: "two") == 2) + #expect(dict.value(forKey: "three") == 3) + } + + @Test func subscriptAccess() throws { + var dict = AtomicDictionary() + dict["one"] = 1 + dict["two"] = 2 + #expect(dict["one"] == 1) + #expect(dict["two"] == 2) + #expect(dict.count == 2) + } + + @Test func setValue() throws { + var dict = AtomicDictionary() + dict.set(value: 1, forKey: "one") + dict.set(value: 2, forKey: "two") + #expect(dict.value(forKey: "one") == 1) + #expect(dict.value(forKey: "two") == 2) + #expect(dict.count == 2) + } + + @Test func removeValue() throws { + var dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) + let removedValue = dict.removeValue(forKey: "two") + #expect(removedValue == 2) + #expect(dict.count == 2) + #expect(dict.value(forKey: "two") == nil) + } + + @Test func removeAllValues() throws { + var dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) + dict.removeAll() + #expect(dict.count == 0) + #expect(dict.isEmpty) + } + + @Test func updateValue() throws { + var dict = AtomicDictionary(["one": 1, "two": 2]) + let oldValue = dict.updateValue(3, forKey: "two") + #expect(oldValue == 2) + #expect(dict.value(forKey: "two") == 3) + } + + @Test func containsKey() throws { + let dict = AtomicDictionary(["one": 1, "two": 2]) + #expect(dict.contains(key: "one")) + #expect(!dict.contains(key: "three")) + } + + @Test func keysAndValues() throws { + let dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) + let keys = dict.keys + let values = dict.values + #expect(keys.contains("one")) + #expect(keys.contains("two")) + #expect(keys.contains("three")) + #expect(values.contains(1)) + #expect(values.contains(2)) + #expect(values.contains(3)) + } + + @Test func forEach() throws { + let dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) + var sum = 0 + for (_, value) in dict { + sum += value + } + #expect(sum == 6) + } + + @Test func filter() throws { + let dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) + let filtered = dict.filter { _, value in + value > 1 + } + #expect(filtered.count == 2) + #expect(filtered.value(forKey: "two") == 2) + #expect(filtered.value(forKey: "three") == 3) + } + + @Test func mergeDictionaries() throws { + var dict = AtomicDictionary(["one": 1, "two": 2]) + let otherDict = ["two": 3, "three": 3] + dict.merge(otherDict) { current, _ in current } + #expect(dict.count == 3) + #expect(dict.value(forKey: "two") == 2) + #expect(dict.value(forKey: "three") == 3) + } + + @Test func mergeAtomicDictionaries() throws { + var dict = AtomicDictionary(["one": 1, "two": 2]) + let otherDict = AtomicDictionary(["two": 3, "three": 3]) + dict.merge(otherDict) { current, _ in current } + #expect(dict.count == 3) + #expect(dict.value(forKey: "two") == 2) + #expect(dict.value(forKey: "three") == 3) + } + + @Test func mapValues() throws { + let dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) + let mapped = dict.mapValues { value in + value * 2 + } + #expect(mapped.count == 3) + #expect(mapped.value(forKey: "one") == 2) + #expect(mapped.value(forKey: "two") == 4) + #expect(mapped.value(forKey: "three") == 6) + } + + @Test func compactMapValues() throws { + let dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) + let compactMapped = dict.compactMapValues { value in + value % 2 == 0 ? nil : value + } + #expect(compactMapped.count == 2) + #expect(compactMapped.value(forKey: "one") == 1) + #expect(compactMapped.value(forKey: "three") == 3) + } + + @Test func equatable() throws { + let dict1 = AtomicDictionary(["one": 1, "two": 2]) + let dict2 = AtomicDictionary(["one": 1, "two": 2]) + let dict3 = AtomicDictionary(["one": 1, "three": 3]) + #expect(dict1 == dict2) + #expect(dict1 != dict3) + } +} From 515e813289d8b8f157914ddf40393ef9534690c5 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 10:13:24 +0800 Subject: [PATCH 20/89] update test From 9497f704ee10f92d972d70e2bdeadcf5af327e8b Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 10:14:45 +0800 Subject: [PATCH 21/89] update for each test From b594ae9a755d5e02260b9bded37f2a71c6b6fb06 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 10:16:54 +0800 Subject: [PATCH 22/89] Update AtomicDictionaryTests.swift --- Utils/Tests/UtilsTests/AtomicDictionaryTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utils/Tests/UtilsTests/AtomicDictionaryTests.swift b/Utils/Tests/UtilsTests/AtomicDictionaryTests.swift index 0139d002..119c313f 100644 --- a/Utils/Tests/UtilsTests/AtomicDictionaryTests.swift +++ b/Utils/Tests/UtilsTests/AtomicDictionaryTests.swift @@ -80,7 +80,7 @@ struct AtomicDictionaryTests { @Test func forEach() throws { let dict = AtomicDictionary(["one": 1, "two": 2, "three": 3]) var sum = 0 - for (_, value) in dict { + dict.forEach { _, value in sum += value } #expect(sum == 6) From b0e16bb5713aebae2beb4dc82abc58d5e08dd3b3 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 11:14:20 +0800 Subject: [PATCH 23/89] Update Networking/Sources/Networking/msquic/QuicClient.swift Co-authored-by: Xiliang Chen --- Networking/Sources/Networking/msquic/QuicClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index afc55e0b..9e33d5b1 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -140,7 +140,7 @@ public class QuicClient: @unchecked Sendable { deinit { close() - clientLogger.info("QuicClient Deinit") + clientLogger.trace("QuicClient Deinit") } } From e54eac4ef0e47ac5c985de55568cc2bf61be50e3 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 11:14:34 +0800 Subject: [PATCH 24/89] Update Networking/Sources/Networking/msquic/QuicClient.swift Co-authored-by: Xiliang Chen --- Networking/Sources/Networking/msquic/QuicClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 9e33d5b1..dd3d89cd 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -134,7 +134,7 @@ public class QuicClient: @unchecked Sendable { quicClient: self, message: QuicMessage(type: .close, data: nil) ) } - clientLogger.info("QuicClient Close") + clientLogger.debug("QuicClient Close") } } From b4a4e25fb497bcb10d9e4a7c8e124cab8c7c3d03 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 11:15:22 +0800 Subject: [PATCH 25/89] Update NetAddr.swift --- Networking/Sources/Networking/msquic/NetAddr.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Networking/Sources/Networking/msquic/NetAddr.swift b/Networking/Sources/Networking/msquic/NetAddr.swift index 58de1692..7a8a8ece 100644 --- a/Networking/Sources/Networking/msquic/NetAddr.swift +++ b/Networking/Sources/Networking/msquic/NetAddr.swift @@ -4,12 +4,6 @@ struct NetAddr: Hashable { var ipAddress: String var port: UInt16 - // Implement the hash(into:) method - func hash(into hasher: inout Hasher) { - hasher.combine(ipAddress) - hasher.combine(port) - } - // Implement the == operator static func == (lhs: NetAddr, rhs: NetAddr) -> Bool { lhs.ipAddress == rhs.ipAddress && lhs.port == rhs.port From f3a42a016928ac7c51b6e9e9df52a28d25cc8356 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 11:16:28 +0800 Subject: [PATCH 26/89] Update QuicConfig.swift --- Networking/Sources/Networking/msquic/QuicConfig.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index e0196b94..ac81ab8d 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -26,9 +26,6 @@ public struct QuicConfig { var certificateFile = QuicCertificateFile() var credConfig = QuicCredentialConfig() - memset(&certificateFile, 0, MemoryLayout.size(ofValue: certificateFile)) - memset(&credConfig, 0, MemoryLayout.size(ofValue: credConfig)) - // Convert certificate and key paths to C strings let certCString = cert.utf8CString let keyFileCString = key.utf8CString From cad71d726d45d5bd32914d04465c16bfff8c2bbd Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 11:40:28 +0800 Subject: [PATCH 27/89] update quic --- .../Sources/Networking/msquic/NetAddr.swift | 6 --- .../Networking/msquic/QuicClient.swift | 3 +- .../Networking/msquic/QuicConnection.swift | 7 +-- .../xcshareddata/xcschemes/NodeTests.xcscheme | 54 +++++++++++++++++++ 4 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 Node/.swiftpm/xcode/xcshareddata/xcschemes/NodeTests.xcscheme diff --git a/Networking/Sources/Networking/msquic/NetAddr.swift b/Networking/Sources/Networking/msquic/NetAddr.swift index 58de1692..7a8a8ece 100644 --- a/Networking/Sources/Networking/msquic/NetAddr.swift +++ b/Networking/Sources/Networking/msquic/NetAddr.swift @@ -4,12 +4,6 @@ struct NetAddr: Hashable { var ipAddress: String var port: UInt16 - // Implement the hash(into:) method - func hash(into hasher: inout Hasher) { - hasher.combine(ipAddress) - hasher.combine(port) - } - // Implement the == operator static func == (lhs: NetAddr, rhs: NetAddr) -> Bool { lhs.ipAddress == rhs.ipAddress && lhs.port == rhs.port diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index afc55e0b..efff46d3 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -52,10 +52,9 @@ public class QuicClient: @unchecked Sendable { func start() throws -> QuicStatus { let status = QuicStatusCode.success.rawValue try loadConfiguration() - connection = QuicConnection( + connection = try QuicConnection( api: api, registration: registration, configuration: configuration, messageHandler: self ) - try connection?.open() try connection?.start(ipAddress: config.ipAddress, port: config.port) return status } diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 5cee856b..e2ba6009 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -32,7 +32,7 @@ public class QuicConnection { registration: HQuic?, configuration: HQuic?, messageHandler: QuicConnectionMessageHandler? = nil - ) { + ) throws { self.api = api self.registration = registration self.configuration = configuration @@ -44,6 +44,7 @@ public class QuicConnection { connection: connection, context: context, event: event ) } + try open() } // Initializer for wrapping an existing connection @@ -85,7 +86,7 @@ public class QuicConnection { } // Opens the connection - func open() throws { + private func open() throws { let status = (api?.pointee.ConnectionOpen( registration, @@ -164,7 +165,7 @@ public class QuicConnection { // Deinitializer to ensure resources are cleaned up deinit { close() - logger.info("QuicConnection Deinit") + logger.trace("QuicConnection Deinit") } } diff --git a/Node/.swiftpm/xcode/xcshareddata/xcschemes/NodeTests.xcscheme b/Node/.swiftpm/xcode/xcshareddata/xcschemes/NodeTests.xcscheme new file mode 100644 index 00000000..8ed21165 --- /dev/null +++ b/Node/.swiftpm/xcode/xcshareddata/xcschemes/NodeTests.xcscheme @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + From a926289d0a37ae9479b50373d128578616c021e9 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 11:49:47 +0800 Subject: [PATCH 28/89] update quic client --- Networking/Sources/Networking/msquic/NetAddr.swift | 5 ----- Networking/Sources/Networking/msquic/QuicConnection.swift | 2 +- Networking/Sources/Networking/msquic/QuicStream.swift | 6 +++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Networking/Sources/Networking/msquic/NetAddr.swift b/Networking/Sources/Networking/msquic/NetAddr.swift index 7a8a8ece..6b709c4c 100644 --- a/Networking/Sources/Networking/msquic/NetAddr.swift +++ b/Networking/Sources/Networking/msquic/NetAddr.swift @@ -3,9 +3,4 @@ import Foundation struct NetAddr: Hashable { var ipAddress: String var port: UInt16 - - // Implement the == operator - static func == (lhs: NetAddr, rhs: NetAddr) -> Bool { - lhs.ipAddress == rhs.ipAddress && lhs.port == rhs.port - } } diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index e2ba6009..187c847e 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -158,7 +158,7 @@ public class QuicConnection { api?.pointee.ConnectionClose(connection) connection = nil } - logger.info("QuicConnection close") + logger.debug("QuicConnection close") } } diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 7f688966..7f95e8f8 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -88,13 +88,13 @@ public class QuicStream { // Closes the stream and cleans up resources func close() { if isClosed.compareExchange(expected: false, desired: true, ordering: .acquiring).exchanged { - streamLogger.info("QuicStream close") streamCallback = nil messageHandler = nil if stream != nil { api?.pointee.StreamClose(stream) stream = nil } + streamLogger.debug("QuicStream close") } } @@ -200,7 +200,7 @@ public class QuicStream { // Deinitializer to ensure resources are cleaned up deinit { close() - streamLogger.info("QuicStream Deinit") + streamLogger.trace("QuicStream Deinit") } } @@ -250,7 +250,7 @@ extension QuicStream { streamLogger.info("[\(String(describing: stream))] Peer shut down") case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: - streamLogger.warning("[\(String(describing: stream))] Peer aborted") + streamLogger.error("[\(String(describing: stream))] Peer aborted") status = (quicStream.api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)) .status From bd61408b52436c5a8d37b7e67c22a0914899f3da Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 14:49:44 +0800 Subject: [PATCH 29/89] update quicconfig --- .../Networking/msquic/QuicClient.swift | 4 +- .../Networking/msquic/QuicConfig.swift | 119 ++++++++---------- 2 files changed, 55 insertions(+), 68 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index c7e83627..26f1bc28 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -175,8 +175,6 @@ extension QuicClient: QuicConnectionMessageHandler { extension QuicClient { private func loadConfiguration() throws { - configuration = try config.loadConfiguration( - api: api, registration: registration - ) + try config.loadConfiguration(api: api, registration: registration, configuration: &configuration) } } diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index ac81ab8d..05ee08b8 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -11,8 +11,9 @@ public struct QuicConfig { public func loadConfiguration( api: UnsafePointer?, - registration: HQuic? - ) throws -> HQuic? { + registration: HQuic?, + configuration: inout HQuic? + ) throws { // Initialize QUIC settings var settings = QuicSettings() settings.IdleTimeoutMs = 10000 @@ -22,69 +23,57 @@ public struct QuicConfig { settings.PeerBidiStreamCount = 1 settings.IsSet.PeerBidiStreamCount = 1 - // Initialize certificate and credential configurations - var certificateFile = QuicCertificateFile() - var credConfig = QuicCredentialConfig() - - // Convert certificate and key paths to C strings - let certCString = cert.utf8CString - let keyFileCString = key.utf8CString - - // Allocate memory for certificate and key paths - let certPointer = UnsafeMutablePointer.allocate(capacity: certCString.count) - let keyFilePointer = UnsafeMutablePointer.allocate(capacity: keyFileCString.count) - - // Copy the C strings to the allocated memory - let certBufferPointer = UnsafeMutableBufferPointer(start: certPointer, count: certCString.count) - _ = certBufferPointer.initialize(from: certCString) - - let keyFileBufferPointer = UnsafeMutableBufferPointer(start: keyFilePointer, count: keyFileCString.count) - _ = keyFileBufferPointer.initialize(from: keyFileCString) - - // Set certificate file paths in QUIC_CERTIFICATE_FILE - certificateFile.CertificateFile = UnsafePointer(certPointer) - certificateFile.PrivateKeyFile = UnsafePointer(keyFilePointer) - - let certificateFilePointer = UnsafeMutablePointer.allocate(capacity: 1) - certificateFilePointer.initialize(to: certificateFile) - - // Configure credentials - credConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE - credConfig.Flags = QUIC_CREDENTIAL_FLAG_NONE - credConfig.CertificateFile = certificateFilePointer - - // Convert ALPN to data buffer - let buffer = Data(alpn.utf8) - let bufferPointer = UnsafeMutablePointer.allocate(capacity: buffer.count) - buffer.copyBytes(to: bufferPointer, count: buffer.count) - - // Ensure memory is deallocated - defer { - certPointer.deallocate() - keyFilePointer.deallocate() - certificateFilePointer.deallocate() - bufferPointer.deallocate() - } - - var alpn = QuicBuffer(Length: UInt32(buffer.count), Buffer: bufferPointer) - - // Open QUIC configuration - var configuration: HQuic? - let status = (api?.pointee.ConfigurationOpen( - registration, &alpn, 1, &settings, UInt32(MemoryLayout.size(ofValue: settings)), - nil, &configuration - )).status - - if status.isFailed { - throw QuicError.invalidStatus(status: status.code) - } - - // Load credentials into the configuration - let configStatus = (api?.pointee.ConfigurationLoadCredential(configuration, &credConfig)).status - if configStatus.isFailed { - throw QuicError.invalidStatus(status: configStatus.code) + // Use withCString to avoid manual memory management + try cert.withCString { certPointer in + try key.withCString { keyPointer in + var certificateFile = QuicCertificateFile( + PrivateKeyFile: keyPointer, CertificateFile: certPointer + ) + + // Use withUnsafePointer to ensure the pointer is valid + try withUnsafePointer(to: &certificateFile) { certFilePointer in + let mutableCertFilePointer = UnsafeMutablePointer(mutating: certFilePointer) + var credConfig = QuicCredentialConfig( + Type: QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE, + Flags: QUIC_CREDENTIAL_FLAG_NONE, + QuicCredentialConfig.__Unnamed_union___Anonymous_field2( + CertificateFile: mutableCertFilePointer + ), + Principal: nil, // Not needed in this context + Reserved: nil, // Not needed in this context + AsyncHandler: nil, // Not needed in this context + AllowedCipherSuites: QUIC_ALLOWED_CIPHER_SUITE_NONE, // Default value + CaCertificateFile: nil // Not needed in this context + ) + + // Convert ALPN to data buffer + let buffer = Data(alpn.utf8) + try buffer.withUnsafeBytes { bufferPointer in + var alpnBuffer = QUIC_BUFFER( + Length: UInt32(buffer.count), + Buffer: UnsafeMutablePointer( + mutating: bufferPointer.bindMemory(to: UInt8.self).baseAddress! + ) + ) + + // Open QUIC configuration + let status = (api?.pointee.ConfigurationOpen( + registration, &alpnBuffer, 1, &settings, UInt32(MemoryLayout.size(ofValue: settings)), + nil, &configuration + )).status + + if status.isFailed { + throw QuicError.invalidStatus(status: status.code) + } + + // Load credentials into the configuration + let configStatus = (api?.pointee.ConfigurationLoadCredential(configuration, &credConfig)).status + if configStatus.isFailed { + throw QuicError.invalidStatus(status: configStatus.code) + } + } + } + } } - - return configuration } } From 950ec282335e0a13c3b22a8f29fbe0bacb2e7b90 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 16:08:25 +0800 Subject: [PATCH 30/89] Update Networking/Sources/Networking/msquic/QuicConfig.swift Co-authored-by: Xiliang Chen --- Networking/Sources/Networking/msquic/QuicConfig.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 05ee08b8..b07fd282 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -31,7 +31,7 @@ public struct QuicConfig { ) // Use withUnsafePointer to ensure the pointer is valid - try withUnsafePointer(to: &certificateFile) { certFilePointer in + try withUnsafeMutablePointer(to: &certificateFile) { certFilePointer in let mutableCertFilePointer = UnsafeMutablePointer(mutating: certFilePointer) var credConfig = QuicCredentialConfig( Type: QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE, From 59abc54ad26ac19330f844a2e88a8f99cc1268ac Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 16:24:10 +0800 Subject: [PATCH 31/89] update quic client --- .../Networking/msquic/QuicClient.swift | 32 +++++++++---------- .../Networking/msquic/QuicConnection.swift | 13 ++++---- .../Networking/msquic/QuicStream.swift | 27 ++++++++-------- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 26f1bc28..73dc4b4f 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -49,6 +49,11 @@ public class QuicClient: @unchecked Sendable { registration = registrationHandle } + deinit { + close() + clientLogger.trace("QuicClient Deinit") + } + func start() throws -> QuicStatus { let status = QuicStatusCode.success.rawValue try loadConfiguration() @@ -70,15 +75,14 @@ public class QuicClient: @unchecked Sendable { throw QuicError.getConnectionFailed } let sendStream: QuicStream - // Check if there is an existing stream of the same kind - if streamKind == .uniquePersistent { + // Check if there is an existing stream of the same kind + = if streamKind == .uniquePersistent + { // If there is, send the message to the existing stream - sendStream = try connection.createOrGetUniquePersistentStream(kind: streamKind) + try connection.createOrGetUniquePersistentStream(kind: streamKind) } else { // If there is not, create a new stream - sendStream = try connection.createCommonEphemeralStream() - // Start the stream - try sendStream.start() + try connection.createCommonEphemeralStream() } return sendStream.send(buffer: message, kind: streamKind) } @@ -89,15 +93,14 @@ public class QuicClient: @unchecked Sendable { throw QuicError.getConnectionFailed } let sendStream: QuicStream - // Check if there is an existing stream of the same kind - if streamKind == .uniquePersistent { + // Check if there is an existing stream of the same kind + = if streamKind == .uniquePersistent + { // If there is, send the message to the existing stream - sendStream = try connection.createOrGetUniquePersistentStream(kind: streamKind) + try connection.createOrGetUniquePersistentStream(kind: streamKind) } else { // If there is not, create a new stream - sendStream = try connection.createCommonEphemeralStream() - // Start the stream - try sendStream.start() + try connection.createCommonEphemeralStream() } return try await sendStream.send(buffer: message) } @@ -136,11 +139,6 @@ public class QuicClient: @unchecked Sendable { clientLogger.debug("QuicClient Close") } } - - deinit { - close() - clientLogger.trace("QuicClient Deinit") - } } extension QuicClient: QuicConnectionMessageHandler { diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 187c847e..4f86a13b 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -66,6 +66,12 @@ public class QuicConnection { } } + // Deinitializer to ensure resources are cleaned up + deinit { + close() + logger.trace("QuicConnection Deinit") + } + // Sets the callback handler for the connection func setCallbackHandler() -> QuicStatus { guard let api, let connection, let configuration else { @@ -108,7 +114,6 @@ public class QuicConnection { } let stream = try QuicStream(api: api, connection: connection, kind, messageHandler: self) uniquePersistentStreams[kind] = stream - try stream.start() return stream } @@ -161,12 +166,6 @@ public class QuicConnection { logger.debug("QuicConnection close") } } - - // Deinitializer to ensure resources are cleaned up - deinit { - close() - logger.trace("QuicConnection Deinit") - } } extension QuicConnection { diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 7f95e8f8..e0310b98 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -41,6 +41,7 @@ public class QuicStream { stream: stream, context: context, event: event ) } + try start() } // Initializer for wrapping an existing stream @@ -60,6 +61,12 @@ public class QuicStream { } } + // Deinitializer to ensure resources are cleaned up + deinit { + close() + streamLogger.trace("QuicStream Deinit") + } + // Opens a stream with the specified kind private func openStream(_: StreamKind = .commonEphemeral) throws { let status = @@ -76,7 +83,7 @@ public class QuicStream { } // Starts the stream - func start() throws { + private func start() throws { try openStream(kind) let status = (api?.pointee.StreamStart(stream, QUIC_STREAM_START_FLAG_NONE)).status if status.isFailed { @@ -122,11 +129,11 @@ public class QuicStream { let messageLength = buffer.count let sendBufferRaw = UnsafeMutableRawPointer.allocate( - byteCount: MemoryLayout.size + messageLength, - alignment: MemoryLayout.alignment + byteCount: MemoryLayout.size + messageLength, + alignment: MemoryLayout.alignment ) - let sendBuffer = sendBufferRaw.assumingMemoryBound(to: QUIC_BUFFER.self) + let sendBuffer = sendBufferRaw.assumingMemoryBound(to: QuicBuffer.self) let bufferPointer = UnsafeMutablePointer.allocate( capacity: messageLength ) @@ -158,11 +165,11 @@ public class QuicStream { let messageLength = buffer.count let sendBufferRaw = UnsafeMutableRawPointer.allocate( - byteCount: MemoryLayout.size + messageLength, - alignment: MemoryLayout.alignment + byteCount: MemoryLayout.size + messageLength, + alignment: MemoryLayout.alignment ) - let sendBuffer = sendBufferRaw.assumingMemoryBound(to: QUIC_BUFFER.self) + let sendBuffer = sendBufferRaw.assumingMemoryBound(to: QuicBuffer.self) let bufferPointer = UnsafeMutablePointer.allocate( capacity: messageLength ) @@ -196,12 +203,6 @@ public class QuicStream { } } } - - // Deinitializer to ensure resources are cleaned up - deinit { - close() - streamLogger.trace("QuicStream Deinit") - } } extension QuicStream { From 4412d3077874a7269167e39d6c85c71bf71ef21f Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Wed, 25 Sep 2024 16:47:55 +0800 Subject: [PATCH 32/89] update quic config --- Networking/Sources/Networking/msquic/QuicConfig.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index b07fd282..134ee112 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -30,14 +30,13 @@ public struct QuicConfig { PrivateKeyFile: keyPointer, CertificateFile: certPointer ) - // Use withUnsafePointer to ensure the pointer is valid + // Use withUnsafeMutablePointer to ensure the pointer is valid try withUnsafeMutablePointer(to: &certificateFile) { certFilePointer in - let mutableCertFilePointer = UnsafeMutablePointer(mutating: certFilePointer) var credConfig = QuicCredentialConfig( Type: QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE, Flags: QUIC_CREDENTIAL_FLAG_NONE, QuicCredentialConfig.__Unnamed_union___Anonymous_field2( - CertificateFile: mutableCertFilePointer + CertificateFile: certFilePointer ), Principal: nil, // Not needed in this context Reserved: nil, // Not needed in this context From e7138eb3c1f70ed34f43b616a990b8c54a29bbd0 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Thu, 26 Sep 2024 12:36:43 +0800 Subject: [PATCH 33/89] update peer2peer --- Networking/Sources/Networking/Peer.swift | 212 ++++++++++++++++++ .../Networking/msquic/QuicClient.swift | 91 ++++---- .../Networking/msquic/QuicConfig.swift | 2 +- .../Networking/msquic/QuicConnection.swift | 34 ++- .../Networking/msquic/QuicListener.swift | 150 +++++++++++++ .../Networking/msquic/QuicServer.swift | 141 ++++++++++++ .../Networking/msquic/QuicStream.swift | 17 +- .../Tests/NetworkingTests/PeerTest.swift | 147 ++++++++++++ .../NetworkingTests/QuicClientTest.swift | 27 ++- .../NetworkingTests/QuicServerTest.swift | 47 ++++ 10 files changed, 772 insertions(+), 96 deletions(-) create mode 100644 Networking/Sources/Networking/Peer.swift create mode 100644 Networking/Sources/Networking/msquic/QuicListener.swift create mode 100644 Networking/Sources/Networking/msquic/QuicServer.swift create mode 100644 Networking/Tests/NetworkingTests/PeerTest.swift create mode 100644 Networking/Tests/NetworkingTests/QuicServerTest.swift diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift new file mode 100644 index 00000000..aa7ca363 --- /dev/null +++ b/Networking/Sources/Networking/Peer.swift @@ -0,0 +1,212 @@ +import Foundation +import Logging +import Utils + +// Logger for Peer +let peerLogger = Logger(label: "PeerServer") + +// Define message types +public enum PeerMessageType: Sendable { + case uniquePersistent + case commonEphemeral + case unknown +} + +// Define PeerMessage protocol +public protocol PeerMessage: Equatable, Sendable { + func getData() -> Data + func getMessageType() -> PeerMessageType +} + +extension PeerMessage { + public func getMessageType() -> PeerMessageType { + .uniquePersistent + } +} + +// Define events +public struct PeerMessageReceived: Event { + public let messageID: Int64 + public let message: QuicMessage +} + +// TODO: add error or remove it +public struct PeerErrorReceived: Event { + public let messageID: Int64? + public let error: QuicError +} + +// Define the Peer class +public final class Peer: @unchecked Sendable { + private let config: QuicConfig + private var quicServer: QuicServer? + private var clients: AtomicDictionary + private let eventBus: EventBus + + public init(config: QuicConfig, eventBus: EventBus) throws { + self.config = config + self.eventBus = eventBus + clients = .init() + quicServer = try QuicServer(config: config, messageHandler: self) + } + + deinit { + clients.removeAll() + quicServer?.close() + peerLogger.info("Peer Deinit") + // Clean up resources if necessary + } + + // Respond to a message with a specific messageID using Data + func respondTo(messageID: Int64, with data: Data) async throws { + try await quicServer?.respondTo(messageID: messageID, with: data) + } + + // Respond to a message with a specific messageID using Data + func respondTo(messageID: Int64, with data: Data) -> QuicStatus { + quicServer?.respondTo(messageID: messageID, with: data) + ?? QuicStatusCode.internalError.rawValue + } + + // Respond to a message with a specific messageID using PeerMessage + func respondTo(messageID: Int64, with message: any PeerMessage) async throws { + let messageType = message.getMessageType() + + try await quicServer?.respondTo( + messageID: messageID, with: message.getData(), + kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral + ) + } + + // Respond to a message with a specific messageID using PeerMessage + func respondTo(messageID: Int64, with message: any PeerMessage) -> QuicStatus { + let messageType = message.getMessageType() + return quicServer? + .respondTo( + messageID: messageID, + with: message.getData(), + kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral + ) + ?? QuicStatusCode.internalError.rawValue + } + + // send message to other peer + func sendMessageToPeer( + message: any PeerMessage, peerAddr: NetAddr + ) async throws -> QuicMessage { + let buffer = message.getData() + let messageType = message.getMessageType() + return try await sendDataToPeer(buffer, to: peerAddr, messageType: messageType) + } + + // send message to other peer + func sendMessageToPeer( + message: any PeerMessage, peerAddr: NetAddr + ) throws -> QuicStatus { + let buffer = message.getData() + let messageType = message.getMessageType() + + return try sendDataToPeer(buffer, to: peerAddr, messageType: messageType) + } + + private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) + async throws -> QuicMessage + { + if let client = clients[peerAddr] { + // Client already exists, use it to send the data + return try await client.send( + message: data, + streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral + ) + } else { + let config = QuicConfig( + id: config.id, cert: config.cert, key: config.key, alpn: config.alpn, + ipAddress: peerAddr.ipAddress, port: peerAddr.port + ) + // Client does not exist, create a new one + let client = try QuicClient(config: config, messageHandler: self) + clients[peerAddr] = client + return try await client.send( + message: data, + streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral + ) + } + } + + private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) + throws -> QuicStatus + { + if let client = clients[peerAddr] { + // Client already exists, use it to send the data + return try client.send( + message: data, + streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral + ) + } else { + let config = QuicConfig( + id: config.id, cert: config.cert, key: config.key, alpn: config.alpn, + ipAddress: peerAddr.ipAddress, port: peerAddr.port + ) + // Client does not exist, create a new one + let client = try QuicClient(config: config, messageHandler: self) + clients[peerAddr] = client + return try client.send( + message: data, + streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral + ) + } + } + + private func removeClient(with peerAddr: NetAddr) { + _ = clients.removeValue(forKey: peerAddr) + } + + func getPeerAddr() -> String { + "\(config.ipAddress):\(config.port)" + } +} + +extension Peer: QuicClientMessageHandler { + public func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) { + switch message.type { + case .close: + peerLogger.info("QuicClient close") + // Use Task to avoid strong reference cycle + Task { [weak self] in + guard let self else { return } + removeClient(with: quicClient.getNetAddr()) + } + default: + break + } + } + + public func didReceiveError(quicClient _: QuicClient, error: QuicError) { + peerLogger.error("Failed to receive message: \(error)") + Task { + await eventBus.publish(PeerErrorReceived(messageID: nil, error: error)) + } + } +} + +extension Peer: QuicServerMessageHandler { + public func didReceiveMessage(quicServer _: QuicServer, messageID: Int64, message: QuicMessage) { + switch message.type { + case .received: + Task { + await eventBus.publish(PeerMessageReceived(messageID: messageID, message: message)) + } + case .shutdownComplete: + break + default: + break + } + } + + public func didReceiveError(quicServer _: QuicServer, messageID: Int64, error: QuicError) { + peerLogger.error("Failed to receive message: \(error)") + Task { + await eventBus.publish(PeerErrorReceived(messageID: messageID, error: error)) + } + } +} diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 73dc4b4f..49050127 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -1,4 +1,3 @@ -import Atomics import Foundation import Logging import msquic @@ -19,7 +18,6 @@ public class QuicClient: @unchecked Sendable { private var connection: QuicConnection? private let config: QuicConfig private weak var messageHandler: QuicClientMessageHandler? - private let isClosed: ManagedAtomic = .init(false) init(config: QuicConfig, messageHandler: QuicClientMessageHandler? = nil) throws { self.config = config @@ -47,21 +45,20 @@ public class QuicClient: @unchecked Sendable { api = boundPointer registration = registrationHandle + try start() } deinit { close() - clientLogger.trace("QuicClient Deinit") + clientLogger.info("QuicClient Deinit") } - func start() throws -> QuicStatus { - let status = QuicStatusCode.success.rawValue + private func start() throws { try loadConfiguration() connection = try QuicConnection( api: api, registration: registration, configuration: configuration, messageHandler: self ) try connection?.start(ipAddress: config.ipAddress, port: config.port) - return status } // Asynchronous send method that waits for a QuicMessage reply @@ -74,16 +71,15 @@ public class QuicClient: @unchecked Sendable { guard let connection else { throw QuicError.getConnectionFailed } - let sendStream: QuicStream - // Check if there is an existing stream of the same kind - = if streamKind == .uniquePersistent - { - // If there is, send the message to the existing stream - try connection.createOrGetUniquePersistentStream(kind: streamKind) - } else { - // If there is not, create a new stream - try connection.createCommonEphemeralStream() - } + let sendStream: QuicStream // Check if there is an existing stream of the same kind + = + if streamKind == .uniquePersistent { + // If there is, send the message to the existing stream + try connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + // If there is not, create a new stream + try connection.createCommonEphemeralStream() + } return sendStream.send(buffer: message, kind: streamKind) } @@ -92,16 +88,15 @@ public class QuicClient: @unchecked Sendable { guard let connection else { throw QuicError.getConnectionFailed } - let sendStream: QuicStream - // Check if there is an existing stream of the same kind - = if streamKind == .uniquePersistent - { - // If there is, send the message to the existing stream - try connection.createOrGetUniquePersistentStream(kind: streamKind) - } else { - // If there is not, create a new stream - try connection.createCommonEphemeralStream() - } + let sendStream: QuicStream // Check if there is an existing stream of the same kind + = + if streamKind == .uniquePersistent { + // If there is, send the message to the existing stream + try connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + // If there is not, create a new stream + try connection.createCommonEphemeralStream() + } return try await sendStream.send(buffer: message) } @@ -110,34 +105,26 @@ public class QuicClient: @unchecked Sendable { } func close() { - if isClosed.compareExchange(expected: false, desired: true, ordering: .acquiring).exchanged { - if let connection { - connection.close() - self.connection = nil - } - - if let configuration { - api?.pointee.ConfigurationClose(configuration) - self.configuration = nil - } + if let connection { + connection.close() + self.connection = nil + } - if let registration { - api?.pointee.RegistrationClose(registration) - self.registration = nil - } + if let configuration { + api?.pointee.ConfigurationClose(configuration) + self.configuration = nil + } - if api != nil { - MsQuicClose(api) - api = nil - } + if let registration { + api?.pointee.RegistrationClose(registration) + self.registration = nil + } - if let messageHandler { - messageHandler.didReceiveMessage( - quicClient: self, message: QuicMessage(type: .close, data: nil) - ) - } - clientLogger.debug("QuicClient Close") + if api != nil { + MsQuicClose(api) + api = nil } + clientLogger.debug("QuicClient Close") } } @@ -173,6 +160,8 @@ extension QuicClient: QuicConnectionMessageHandler { extension QuicClient { private func loadConfiguration() throws { - try config.loadConfiguration(api: api, registration: registration, configuration: &configuration) + try config.loadConfiguration( + api: api, registration: registration, configuration: &configuration + ) } } diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 134ee112..485e5409 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 10000 + settings.IdleTimeoutMs = 3000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 4f86a13b..55949c68 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -1,4 +1,3 @@ -import Atomics import Foundation import Logging import msquic @@ -24,7 +23,6 @@ public class QuicConnection { private var commonEphemeralStreams: AtomicArray private weak var messageHandler: QuicConnectionMessageHandler? private var connectionCallback: ConnectionCallback? - private let isClosed: ManagedAtomic = .init(false) // Initializer for creating a new connection init( @@ -119,7 +117,7 @@ public class QuicConnection { // Creates a common ephemeral stream func createCommonEphemeralStream() throws -> QuicStream { - let stream = try QuicStream(api: api, connection: connection, .commonEphemeral, messageHandler: self) + let stream: QuicStream = try QuicStream(api: api, connection: connection, .commonEphemeral, messageHandler: self) commonEphemeralStreams.append(stream) return stream } @@ -148,23 +146,21 @@ public class QuicConnection { // Closes the connection and cleans up resources func close() { - if isClosed.compareExchange(expected: false, desired: true, ordering: .acquiring).exchanged { - connectionCallback = nil - messageHandler = nil - for stream in commonEphemeralStreams { - stream.close() - } - commonEphemeralStreams.removeAll() - for stream in uniquePersistentStreams.values { - stream.close() - } - uniquePersistentStreams.removeAll() - if connection != nil { - api?.pointee.ConnectionClose(connection) - connection = nil - } - logger.debug("QuicConnection close") + connectionCallback = nil + messageHandler = nil + for stream in commonEphemeralStreams { + stream.close() + } + commonEphemeralStreams.removeAll() + for stream in uniquePersistentStreams.values { + stream.close() + } + uniquePersistentStreams.removeAll() + if connection != nil { + api?.pointee.ConnectionClose(connection) + connection = nil } + logger.debug("QuicConnection close") } } diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift new file mode 100644 index 00000000..c6ae77d0 --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -0,0 +1,150 @@ +import Foundation +import Logging +import msquic +import Utils + +let listenLogger: Logger = .init(label: "QuicListener") + +public protocol QuicListenerMessageHandler: AnyObject { + func didReceiveMessage(connection: QuicConnection, stream: QuicStream, message: QuicMessage) + func didReceiveError(connection: QuicConnection, stream: QuicStream, error: QuicError) +} + +public class QuicListener { + private var api: UnsafePointer? + private var registration: HQuic? + private var configuration: HQuic? + private var config: QuicConfig + private var listener: HQuic? + private var connections: AtomicArray = .init() + public weak var messageHandler: QuicListenerMessageHandler? + + public init( + api: UnsafePointer?, + registration: HQuic?, + configuration: HQuic?, + config: QuicConfig, + messageHandler: QuicListenerMessageHandler? = nil + ) { + self.api = api + self.registration = registration + self.configuration = configuration + self.config = config + self.messageHandler = messageHandler + } + + public func openListener(port: UInt16) throws { + var listenerHandle: HQuic? + let status = (api?.pointee.ListenerOpen( + registration, + { listener, context, event -> QuicStatus in + QuicListener.serverListenerCallback( + listener: listener, context: context, event: event + ) + }, + UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()), &listenerHandle + )).status + + if status.isFailed { + throw QuicError.invalidStatus(status: status.code) + } + + listener = listenerHandle + + let buffer = Data(config.alpn.utf8) + let bufferPointer = UnsafeMutablePointer.allocate(capacity: buffer.count) + buffer.copyBytes(to: bufferPointer, count: buffer.count) + defer { + free(bufferPointer) + } + var alpn = QuicBuffer(Length: UInt32(buffer.count), Buffer: bufferPointer) + var address = QUIC_ADDR() + + QuicAddrSetFamily(&address, QUIC_ADDRESS_FAMILY(QUIC_ADDRESS_FAMILY_UNSPEC)) + QuicAddrSetPort(&address, port) + let startStatus: QuicStatus = (api?.pointee.ListenerStart(listener, &alpn, 1, &address)).status + + if startStatus.isFailed { + throw QuicError.invalidStatus(status: startStatus.code) + } + } + + private static func serverListenerCallback( + listener _: HQuic?, context: UnsafeMutableRawPointer?, + event: UnsafePointer? + ) -> QuicStatus { + var status: QuicStatus = QuicStatusCode.notSupported.rawValue + guard let context, let event else { + return status + } + let listener: QuicListener = Unmanaged.fromOpaque(context).takeUnretainedValue() + listenLogger.info("Server listener callback type \(event.pointee.Type.rawValue)") + switch event.pointee.Type { + case QUIC_LISTENER_EVENT_NEW_CONNECTION: + listenLogger.info("New connection") + let connection: HQuic = event.pointee.NEW_CONNECTION.Connection + guard let api = listener.api else { + return status + } + + let quicConnection = QuicConnection( + api: api, + registration: listener.registration, + configuration: listener.configuration, + connection: connection, + messageHandler: listener + ) + listener.connections.append(quicConnection) + status = quicConnection.setCallbackHandler() + + default: + break + } + return status + } + + public func close() { + if let listener { + api?.pointee.ListenerClose(listener) + self.listener = nil + } + closeAllConnections() + } + + private func closeAllConnections() { + for connection in connections { + connection.close() + } + connections.removeAll() + } +} + +extension QuicListener: QuicConnectionMessageHandler { + public func didReceiveMessage( + connection: QuicConnection, stream: QuicStream?, message: QuicMessage + ) { + switch message.type { + case .shutdownComplete: + listenLogger.info("Connection shutdown complete") + removeConnection(connection) + case .received: + if let stream { + messageHandler?.didReceiveMessage(connection: connection, stream: stream, message: message) + } + default: + break + } + } + + public func didReceiveError( + connection: QuicConnection, stream: QuicStream, error: QuicError + ) { + listenLogger.error("Failed to receive message: \(error)") + messageHandler?.didReceiveError(connection: connection, stream: stream, error: error) + } + + private func removeConnection(_ connection: QuicConnection) { + connection.close() + connections.removeAll(where: { $0 === connection }) + } +} diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift new file mode 100644 index 00000000..1517dd7b --- /dev/null +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -0,0 +1,141 @@ +import Atomics +import Foundation +import Logging +import msquic +import Utils + +let serverLogger: Logger = .init(label: "QuicServer") + +public protocol QuicServerMessageHandler: AnyObject { + func didReceiveMessage(quicServer: QuicServer, messageID: Int64, message: QuicMessage) + func didReceiveError(quicServer: QuicServer, messageID: Int64, error: QuicError) +} + +public final class QuicServer: @unchecked Sendable { + private var api: UnsafePointer? + private var registration: HQuic? + private var configuration: HQuic? + private var listener: QuicListener? + private let config: QuicConfig + private weak var messageHandler: QuicServerMessageHandler? + private var pendingMessages: AtomicDictionary + + init(config: QuicConfig, messageHandler: QuicServerMessageHandler? = nil) throws { + self.config = config + self.messageHandler = messageHandler + pendingMessages = .init() + var rawPointer: UnsafeRawPointer? + let status: UInt32 = MsQuicOpenVersion(2, &rawPointer) + + if QuicStatus(status).isFailed { + throw QuicError.invalidStatus(status: status.code) + } + guard + let boundPointer: UnsafePointer = rawPointer?.assumingMemoryBound( + to: QuicApiTable.self + ) + else { + throw QuicError.getApiFailed + } + + var registrationHandle: HQuic? + let registrationStatus = boundPointer.pointee.RegistrationOpen(nil, ®istrationHandle) + if QuicStatus(registrationStatus).isFailed { + throw QuicError.invalidStatus(status: registrationStatus.code) + } + + api = boundPointer + registration = registrationHandle + try loadConfiguration() + listener = QuicListener( + api: api, registration: registration, configuration: configuration, config: config, + messageHandler: self + ) + try start() + } + + deinit { + close() + serverLogger.info("QuicServer Deinit") + } + + private func start() throws { + try listener?.openListener(port: config.port) + } + + func close() { + listener?.close() + if configuration != nil { + api?.pointee.ConfigurationClose(configuration) + configuration = nil + } + if registration != nil { + api?.pointee.RegistrationClose(registration) + registration = nil + } + if api != nil { + MsQuicClose(api) + api = nil + } + serverLogger.info("QuicServer Close") + } + + // Respond to a message with a specific messageID using Data + func respondTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) -> QuicStatus { + var status = QuicStatusCode.internalError.rawValue + if let (_, stream) = pendingMessages[messageID] { + let streamKind = kind ?? stream.kind + status = stream.send(buffer: data, kind: streamKind) + _ = pendingMessages.removeValue(forKey: messageID) + } else { + peerLogger.error("Message not found") + } + return status + } + + // Respond to a message with a specific messageID using Data + func respondTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) async throws { + if let (_, stream) = pendingMessages[messageID] { + let streamKind = kind ?? stream.kind + let quicMessage = try await stream.send(buffer: data, kind: streamKind) + peerLogger.info("Message sent: \(quicMessage)") + _ = pendingMessages.removeValue(forKey: messageID) + } else { + peerLogger.error("Message not found") + throw QuicError.messageNotFound + } + } +} + +extension QuicServer: QuicListenerMessageHandler { + public func didReceiveMessage( + connection: QuicConnection, stream: QuicStream, message: QuicMessage + ) { + switch message.type { + case .received: + processPendingMessage(connection: connection, stream: stream, message: message) + default: + break + } + } + + public func didReceiveError( + connection _: QuicConnection, stream _: QuicStream, error: QuicError + ) { + logger.error("Failed to receive message: \(error)") + } + + private func processPendingMessage( + connection: QuicConnection, stream: QuicStream, message: QuicMessage + ) { + let messageID = Int64(Date().timeIntervalSince1970 * 1000) + pendingMessages[messageID] = (connection, stream) + messageHandler?.didReceiveMessage(quicServer: self, messageID: messageID, message: message) + } +} + +extension QuicServer { + private func loadConfiguration() throws { + try config.loadConfiguration(api: api, registration: registration, configuration: &configuration) + } +} diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index e0310b98..6d669775 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -1,4 +1,3 @@ -import Atomics import Foundation import Logging import msquic @@ -24,8 +23,6 @@ public class QuicStream { private weak var messageHandler: QuicStreamMessageHandler? private var streamCallback: StreamCallback? private var sendCompletion: CheckedContinuation? - private let isClosed: ManagedAtomic = .init(false) - // Initializer for creating a new stream init( api: UnsafePointer?, connection: HQuic?, @@ -94,15 +91,13 @@ public class QuicStream { // Closes the stream and cleans up resources func close() { - if isClosed.compareExchange(expected: false, desired: true, ordering: .acquiring).exchanged { - streamCallback = nil - messageHandler = nil - if stream != nil { - api?.pointee.StreamClose(stream) - stream = nil - } - streamLogger.debug("QuicStream close") + streamCallback = nil + messageHandler = nil + if stream != nil { + api?.pointee.StreamClose(stream) + stream = nil } + streamLogger.debug("QuicStream close") } // Sets the callback handler for the stream diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift new file mode 100644 index 00000000..ff007f8e --- /dev/null +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -0,0 +1,147 @@ +import Foundation +import NIO +import Testing +import Utils + +@testable import Networking + +#if os(macOS) + import CoreFoundation + import Security + + struct Message: PeerMessage { + public let data: Data + public init(data: Data) { + self.data = data + } + + public func getData() -> Data { + data + } + } + + let cert = Bundle.module.path(forResource: "server", ofType: "cert")! + let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! + + final class PeerTests { + @Test func startPeer() async throws { + do { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + // Example instantiation of Peer with EventBus + let eventBus = EventBus() + let peer = try Peer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), + eventBus: eventBus + ) + do { + let quicmessage = try await peer.sendMessageToPeer( + message: Message(data: Data("Hello, World!".utf8)), + peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4569) + ) + print("Peer message got: \(quicmessage)") + } catch { + print("Failed to send: \(error)") + } + + // Example subscription to PeerMessageReceived + _ = await eventBus.subscribe(PeerMessageReceived.self) { event in + print( + "Received message from peer messageID: \(event.messageID), message: \(event.message)" + ) + let status = peer.respondTo( + messageID: event.messageID, with: event.message.data! + ) + print("Peer sent: \(status)") + } + + // Example subscription to PeerErrorReceived + _ = await eventBus.subscribe(PeerErrorReceived.self) { event in + print( + "Received error from peer messageID: \(event.messageID ?? -1), error: \(event.error)" + ) + } + + try await group.next().scheduleTask(in: .seconds(20)) {}.futureResult.get() + + } catch { + print("Failed to start peer: \(error)") + } + } + + @Test func testPeerCommunication() async throws { + do { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let eventBus1 = EventBus() + let eventBus2 = EventBus() + + // Create two Peer instances + let peer1 = try Peer( + config: QuicConfig( + id: "public-key1", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), + eventBus: eventBus1 + ) + + let peer2 = try Peer( + config: QuicConfig( + id: "public-key2", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4569 + ), + eventBus: eventBus2 + ) + + // Subscribe to PeerMessageReceived for peer1 + _ = await eventBus1.subscribe(PeerMessageReceived.self) { event in + print( + "Peer1 received message from messageID: \(event.messageID), message: \(event.message)" + ) + let status = peer1.respondTo(messageID: event.messageID, with: Message(data: event.message.data!)) + print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") + } + + // Subscribe to PeerMessageReceived for peer2 + _ = await eventBus2.subscribe(PeerMessageReceived.self) { event in + print( + "Peer2 received message from messageID: \(event.messageID), message: \(event.message)" + ) + let status = peer2.respondTo(messageID: event.messageID, with: Message(data: event.message.data!)) + print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") + } + + // Schedule message sending after 5 seconds + _ = try await group.next().scheduleTask(in: .seconds(2)) { + Task { + do { + for i in 1 ... 5 { + let messageToPeer2 = try await peer1.sendMessageToPeer( + message: Message(data: Data("Hello from Peer1 - Message \(i)".utf8)), + peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4569) + ) + print("Peer1 sent message \(i): \(messageToPeer2)") + + let messageToPeer1 = try await peer2.sendMessageToPeer( + message: Message(data: Data("Hello from Peer2 - Message \(i)".utf8)), + peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4568) + ) + print("Peer2 sent message \(i): \(messageToPeer1)") + } + } catch { + print("Failed to send message: \(error)") + } + } + }.futureResult.get() + + try await group.next().scheduleTask(in: .seconds(20)) { + print("scheduleTask 10s") + }.futureResult.get() + + } catch { + print("Failed about peer communication test: \(error)") + } + } + } +#endif diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index cb8bea72..56ccc233 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -10,18 +10,16 @@ import Testing final class QuicClientTests { @Test func start() async throws { - do { - let cert = Bundle.module.path(forResource: "server", ofType: "cert")! - let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let quicClient = try QuicClient( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4569 - ) + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let cert = Bundle.module.path(forResource: "server", ofType: "cert")! + let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! + let quicClient = try QuicClient( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4569 ) - let status = try quicClient.start() - print(status) + ) + do { let message1 = try await quicClient.send( message: Data("Hello, World!".utf8), streamKind: .uniquePersistent ) @@ -39,13 +37,14 @@ import Testing ) print("Client received 4: \(message4)") - try await group.next().scheduleTask(in: .seconds(5)) { - print("scheduleTask: 5s") - }.futureResult.get() } catch { // Handle the error if sending the message fails or if the connection fails print("Failed about quic client: \(error)") } + + try await group.next().scheduleTask(in: .seconds(5)) { + print("scheduleTask: 5s") + }.futureResult.get() } } #endif diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift new file mode 100644 index 00000000..d093b8f3 --- /dev/null +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -0,0 +1,47 @@ +import Foundation +import NIO +import Testing + +@testable import Networking + +#if os(macOS) + import CoreFoundation + import Security + + final class QuicServerTests { + @Test func start() throws { + do { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + _ = try QuicServer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), messageHandler: self + ) + try group.next().scheduleTask(in: .seconds(5)) {}.futureResult.wait() + } catch { + print("Failed to start quic server: \(error)") + } + } + } + + extension QuicServerTests: QuicServerMessageHandler { + func didReceiveMessage(quicServer: QuicServer, messageID: Int64, message: QuicMessage) { + switch message.type { + case .received: + print("Server received message with ID \(messageID): \(message)") + _ = quicServer.respondTo(messageID: messageID, with: message.data!) + case .shutdownComplete: + print("Server shutdown complete") + case .unknown: + print("Server unknown") + default: + break + } + } + + func didReceiveError(quicServer _: QuicServer, messageID _: Int64, error: QuicError) { + print("Server error: \(error)") + } + } +#endif From bb7fc62a540755b449da5e8bdd0969a048bbdaea Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Thu, 26 Sep 2024 13:59:04 +0800 Subject: [PATCH 34/89] update quic server & peer --- Networking/Sources/Networking/Peer.swift | 1 + .../Networking/msquic/QuicListener.swift | 2 -- .../Networking/msquic/QuicServer.swift | 7 ++-- .../Tests/NetworkingTests/PeerTest.swift | 34 +++++++++++++------ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index aa7ca363..83fd42a5 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -109,6 +109,7 @@ public final class Peer: @unchecked Sendable { return try sendDataToPeer(buffer, to: peerAddr, messageType: messageType) } + // send message to other peer wait for response quicMessage private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) async throws -> QuicMessage { diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index c6ae77d0..8f655f54 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -78,7 +78,6 @@ public class QuicListener { return status } let listener: QuicListener = Unmanaged.fromOpaque(context).takeUnretainedValue() - listenLogger.info("Server listener callback type \(event.pointee.Type.rawValue)") switch event.pointee.Type { case QUIC_LISTENER_EVENT_NEW_CONNECTION: listenLogger.info("New connection") @@ -125,7 +124,6 @@ extension QuicListener: QuicConnectionMessageHandler { ) { switch message.type { case .shutdownComplete: - listenLogger.info("Connection shutdown complete") removeConnection(connection) case .received: if let stream { diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 1517dd7b..ec07ef80 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -56,7 +56,7 @@ public final class QuicServer: @unchecked Sendable { deinit { close() - serverLogger.info("QuicServer Deinit") + serverLogger.trace("QuicServer Deinit") } private func start() throws { @@ -64,7 +64,10 @@ public final class QuicServer: @unchecked Sendable { } func close() { - listener?.close() + if listener != nil { + listener?.close() + listener = nil + } if configuration != nil { api?.pointee.ConfigurationClose(configuration) configuration = nil diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index ff007f8e..222a5283 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -95,36 +95,44 @@ import Utils ) // Subscribe to PeerMessageReceived for peer1 - _ = await eventBus1.subscribe(PeerMessageReceived.self) { event in + let token1 = await eventBus1.subscribe(PeerMessageReceived.self) { event in print( "Peer1 received message from messageID: \(event.messageID), message: \(event.message)" ) - let status = peer1.respondTo(messageID: event.messageID, with: Message(data: event.message.data!)) + let status = peer1.respondTo( + messageID: event.messageID, with: Message(data: event.message.data!) + ) print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") } // Subscribe to PeerMessageReceived for peer2 - _ = await eventBus2.subscribe(PeerMessageReceived.self) { event in + let token2 = await eventBus2.subscribe(PeerMessageReceived.self) { event in print( "Peer2 received message from messageID: \(event.messageID), message: \(event.message)" ) - let status = peer2.respondTo(messageID: event.messageID, with: Message(data: event.message.data!)) + let status = peer2.respondTo( + messageID: event.messageID, with: Message(data: event.message.data!) + ) print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") } - // Schedule message sending after 5 seconds + // Schedule message sending after 5 seconds _ = try await group.next().scheduleTask(in: .seconds(2)) { Task { do { for i in 1 ... 5 { let messageToPeer2 = try await peer1.sendMessageToPeer( - message: Message(data: Data("Hello from Peer1 - Message \(i)".utf8)), + message: Message( + data: Data("Hello from Peer1 - Message \(i)".utf8) + ), peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4569) ) print("Peer1 sent message \(i): \(messageToPeer2)") let messageToPeer1 = try await peer2.sendMessageToPeer( - message: Message(data: Data("Hello from Peer2 - Message \(i)".utf8)), + message: Message( + data: Data("Hello from Peer2 - Message \(i)".utf8) + ), peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4568) ) print("Peer2 sent message \(i): \(messageToPeer1)") @@ -135,10 +143,16 @@ import Utils } }.futureResult.get() - try await group.next().scheduleTask(in: .seconds(20)) { - print("scheduleTask 10s") + _ = try await group.next().scheduleTask(in: .seconds(5)) { + Task { + await eventBus1.unsubscribe(token: token1) + await eventBus2.unsubscribe(token: token2) + print("eventBus unsubscribe") + } + }.futureResult.get() + try await group.next().scheduleTask(in: .seconds(5)) { + print("scheduleTask end") }.futureResult.get() - } catch { print("Failed about peer communication test: \(error)") } From bb38cdc590082d76b5f35cc0ec5751c842112447 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 27 Sep 2024 06:30:04 +0800 Subject: [PATCH 35/89] Update Networking/Sources/Networking/Peer.swift Co-authored-by: Xiliang Chen --- Networking/Sources/Networking/Peer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 83fd42a5..19a5ad08 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -53,7 +53,7 @@ public final class Peer: @unchecked Sendable { deinit { clients.removeAll() quicServer?.close() - peerLogger.info("Peer Deinit") + peerLogger.trace("Peer Deinit") // Clean up resources if necessary } From 1f11bedbebbc7be34086e3f4cf06b7e8b32eef66 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 27 Sep 2024 06:30:33 +0800 Subject: [PATCH 36/89] Update Networking/Sources/Networking/Peer.swift Co-authored-by: Xiliang Chen --- Networking/Sources/Networking/Peer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 19a5ad08..618e22c5 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -171,7 +171,7 @@ extension Peer: QuicClientMessageHandler { public func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) { switch message.type { case .close: - peerLogger.info("QuicClient close") + peerLogger.trace("QuicClient close") // Use Task to avoid strong reference cycle Task { [weak self] in guard let self else { return } From e717bf4c8b8b4a2b92ed733acc437ae43717ccf5 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 27 Sep 2024 06:31:13 +0800 Subject: [PATCH 37/89] Update Networking/Sources/Networking/msquic/QuicListener.swift Co-authored-by: Xiliang Chen --- Networking/Sources/Networking/msquic/QuicListener.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 8f655f54..aff08a62 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -80,7 +80,7 @@ public class QuicListener { let listener: QuicListener = Unmanaged.fromOpaque(context).takeUnretainedValue() switch event.pointee.Type { case QUIC_LISTENER_EVENT_NEW_CONNECTION: - listenLogger.info("New connection") + listenLogger.debug("New connection") let connection: HQuic = event.pointee.NEW_CONNECTION.Connection guard let api = listener.api else { return status From 846bed018428ec6ccdd387c2f4d80c20902d908f Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 27 Sep 2024 15:06:10 +0800 Subject: [PATCH 38/89] update peer --- Networking/Sources/Networking/Peer.swift | 48 +++++++++---------- .../Networking/msquic/QuicServer.swift | 2 +- .../Tests/NetworkingTests/PeerTest.swift | 14 +++--- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 618e22c5..fe22b9d4 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -36,17 +36,17 @@ public struct PeerErrorReceived: Event { public let error: QuicError } -// Define the Peer class -public final class Peer: @unchecked Sendable { +// Define the Peer actor +public actor Peer { private let config: QuicConfig private var quicServer: QuicServer? - private var clients: AtomicDictionary + private var clients: [NetAddr: QuicClient] private let eventBus: EventBus - public init(config: QuicConfig, eventBus: EventBus) throws { + public init(config: QuicConfig, eventBus: EventBus) async throws { self.config = config self.eventBus = eventBus - clients = .init() + clients = [:] quicServer = try QuicServer(config: config, messageHandler: self) } @@ -58,28 +58,13 @@ public final class Peer: @unchecked Sendable { } // Respond to a message with a specific messageID using Data - func respondTo(messageID: Int64, with data: Data) async throws { - try await quicServer?.respondTo(messageID: messageID, with: data) - } - - // Respond to a message with a specific messageID using Data - func respondTo(messageID: Int64, with data: Data) -> QuicStatus { + func respondTo(messageID: Int64, with data: Data) async -> QuicStatus { quicServer?.respondTo(messageID: messageID, with: data) ?? QuicStatusCode.internalError.rawValue } // Respond to a message with a specific messageID using PeerMessage - func respondTo(messageID: Int64, with message: any PeerMessage) async throws { - let messageType = message.getMessageType() - - try await quicServer?.respondTo( - messageID: messageID, with: message.getData(), - kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral - ) - } - - // Respond to a message with a specific messageID using PeerMessage - func respondTo(messageID: Int64, with message: any PeerMessage) -> QuicStatus { + func respondTo(messageID: Int64, with message: any PeerMessage) async -> QuicStatus { let messageType = message.getMessageType() return quicServer? .respondTo( @@ -90,6 +75,16 @@ public final class Peer: @unchecked Sendable { ?? QuicStatusCode.internalError.rawValue } + // Respond to a message with a specific messageID using PeerMessage (async throws) + func respondToPeerMessage(messageID: Int64, with message: any PeerMessage) async throws { + let messageType = message.getMessageType() + + try await quicServer?.respondTo( + messageID: messageID, with: message.getData(), + kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral + ) + } + // send message to other peer func sendMessageToPeer( message: any PeerMessage, peerAddr: NetAddr @@ -167,15 +162,15 @@ public final class Peer: @unchecked Sendable { } } -extension Peer: QuicClientMessageHandler { +// QuicClientMessageHandler methods +extension Peer: @preconcurrency QuicClientMessageHandler { public func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) { switch message.type { case .close: peerLogger.trace("QuicClient close") - // Use Task to avoid strong reference cycle Task { [weak self] in guard let self else { return } - removeClient(with: quicClient.getNetAddr()) + await removeClient(with: quicClient.getNetAddr()) } default: break @@ -190,7 +185,8 @@ extension Peer: QuicClientMessageHandler { } } -extension Peer: QuicServerMessageHandler { +// QuicServerMessageHandler methods +extension Peer: @preconcurrency QuicServerMessageHandler { public func didReceiveMessage(quicServer _: QuicServer, messageID: Int64, message: QuicMessage) { switch message.type { case .received: diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index ec07ef80..3310caab 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -100,9 +100,9 @@ public final class QuicServer: @unchecked Sendable { func respondTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) async throws { if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind + _ = pendingMessages.removeValue(forKey: messageID) let quicMessage = try await stream.send(buffer: data, kind: streamKind) peerLogger.info("Message sent: \(quicMessage)") - _ = pendingMessages.removeValue(forKey: messageID) } else { peerLogger.error("Message not found") throw QuicError.messageNotFound diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 222a5283..afa81315 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -29,7 +29,7 @@ import Utils let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) // Example instantiation of Peer with EventBus let eventBus = EventBus() - let peer = try Peer( + let peer = try await Peer( config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", ipAddress: "127.0.0.1", port: 4568 @@ -51,8 +51,8 @@ import Utils print( "Received message from peer messageID: \(event.messageID), message: \(event.message)" ) - let status = peer.respondTo( - messageID: event.messageID, with: event.message.data! + let status: QuicStatus = await peer.respondTo( + messageID: event.messageID, with: Message(data: event.message.data!) ) print("Peer sent: \(status)") } @@ -78,7 +78,7 @@ import Utils let eventBus2 = EventBus() // Create two Peer instances - let peer1 = try Peer( + let peer1 = try await Peer( config: QuicConfig( id: "public-key1", cert: cert, key: keyFile, alpn: "sample", ipAddress: "127.0.0.1", port: 4568 @@ -86,7 +86,7 @@ import Utils eventBus: eventBus1 ) - let peer2 = try Peer( + let peer2 = try await Peer( config: QuicConfig( id: "public-key2", cert: cert, key: keyFile, alpn: "sample", ipAddress: "127.0.0.1", port: 4569 @@ -99,7 +99,7 @@ import Utils print( "Peer1 received message from messageID: \(event.messageID), message: \(event.message)" ) - let status = peer1.respondTo( + let status: QuicStatus = await peer1.respondTo( messageID: event.messageID, with: Message(data: event.message.data!) ) print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") @@ -110,7 +110,7 @@ import Utils print( "Peer2 received message from messageID: \(event.messageID), message: \(event.message)" ) - let status = peer2.respondTo( + let status: QuicStatus = await peer2.respondTo( messageID: event.messageID, with: Message(data: event.message.data!) ) print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") From d2577eeb025305627ff45c7867c80ce66c325161 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 27 Sep 2024 15:58:27 +0800 Subject: [PATCH 39/89] update quic listener --- Networking/Sources/Networking/Peer.swift | 3 + .../Networking/msquic/QuicListener.swift | 70 +++++++++++-------- .../Networking/msquic/QuicServer.swift | 8 +-- scripts/bandersnatch.sh | 2 +- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index fe22b9d4..f94b5783 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -51,6 +51,9 @@ public actor Peer { } deinit { + for client in clients.values { + client.close() + } clients.removeAll() quicServer?.close() peerLogger.trace("Peer Deinit") diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index aff08a62..521529c5 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -25,47 +25,54 @@ public class QuicListener { configuration: HQuic?, config: QuicConfig, messageHandler: QuicListenerMessageHandler? = nil - ) { + ) throws { self.api = api self.registration = registration self.configuration = configuration self.config = config self.messageHandler = messageHandler + // try openListener(port: config.port, listener: &listener) } - public func openListener(port: UInt16) throws { - var listenerHandle: HQuic? - let status = (api?.pointee.ListenerOpen( - registration, - { listener, context, event -> QuicStatus in - QuicListener.serverListenerCallback( - listener: listener, context: context, event: event - ) - }, - UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()), &listenerHandle - )).status - - if status.isFailed { + private func openListener(port: UInt16, listener: inout HQuic?) throws { + // Open the listener + let status = + (api?.pointee.ListenerOpen( + registration, + { listener, context, event -> QuicStatus in + QuicListener.serverListenerCallback( + listener: listener, context: context, event: event + ) + }, + UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()), &listener + )).status + + guard status.isSucceeded else { throw QuicError.invalidStatus(status: status.code) } - listener = listenerHandle + // Prepare ALPN buffer + let alpnData = Data(config.alpn.utf8) + try alpnData.withUnsafeBytes { bufferPointer in + var alpnBuffer = QuicBuffer( + Length: UInt32(bufferPointer.count), + Buffer: UnsafeMutablePointer( + mutating: bufferPointer.bindMemory(to: UInt8.self).baseAddress! + ) + ) - let buffer = Data(config.alpn.utf8) - let bufferPointer = UnsafeMutablePointer.allocate(capacity: buffer.count) - buffer.copyBytes(to: bufferPointer, count: buffer.count) - defer { - free(bufferPointer) - } - var alpn = QuicBuffer(Length: UInt32(buffer.count), Buffer: bufferPointer) - var address = QUIC_ADDR() + // Prepare address + var address = QUIC_ADDR() + QuicAddrSetFamily(&address, QUIC_ADDRESS_FAMILY(QUIC_ADDRESS_FAMILY_UNSPEC)) + QuicAddrSetPort(&address, port) - QuicAddrSetFamily(&address, QUIC_ADDRESS_FAMILY(QUIC_ADDRESS_FAMILY_UNSPEC)) - QuicAddrSetPort(&address, port) - let startStatus: QuicStatus = (api?.pointee.ListenerStart(listener, &alpn, 1, &address)).status + // Start the listener + let startStatus: QuicStatus = + (api?.pointee.ListenerStart(listener, &alpnBuffer, 1, &address)).status - if startStatus.isFailed { - throw QuicError.invalidStatus(status: startStatus.code) + guard startStatus.isSucceeded else { + throw QuicError.invalidStatus(status: startStatus.code) + } } } @@ -77,7 +84,8 @@ public class QuicListener { guard let context, let event else { return status } - let listener: QuicListener = Unmanaged.fromOpaque(context).takeUnretainedValue() + let listener: QuicListener = Unmanaged.fromOpaque(context) + .takeUnretainedValue() switch event.pointee.Type { case QUIC_LISTENER_EVENT_NEW_CONNECTION: listenLogger.debug("New connection") @@ -127,7 +135,9 @@ extension QuicListener: QuicConnectionMessageHandler { removeConnection(connection) case .received: if let stream { - messageHandler?.didReceiveMessage(connection: connection, stream: stream, message: message) + messageHandler?.didReceiveMessage( + connection: connection, stream: stream, message: message + ) } default: break diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 3310caab..d4b697c5 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -43,15 +43,13 @@ public final class QuicServer: @unchecked Sendable { if QuicStatus(registrationStatus).isFailed { throw QuicError.invalidStatus(status: registrationStatus.code) } - api = boundPointer registration = registrationHandle try loadConfiguration() - listener = QuicListener( + listener = try QuicListener( api: api, registration: registration, configuration: configuration, config: config, messageHandler: self ) - try start() } deinit { @@ -59,10 +57,6 @@ public final class QuicServer: @unchecked Sendable { serverLogger.trace("QuicServer Deinit") } - private func start() throws { - try listener?.openListener(port: config.port) - } - func close() { if listener != nil { listener?.close() diff --git a/scripts/bandersnatch.sh b/scripts/bandersnatch.sh index 6a8967b3..d16c75d5 100755 --- a/scripts/bandersnatch.sh +++ b/scripts/bandersnatch.sh @@ -10,7 +10,7 @@ mkdir -p .lib cd Utils/Sources/bandersnatch || { echo "directory not found"; exit 1; } -cargo build --release --lib +cargo build --release --lib --target aarch64-apple-darwin cp target/release/libbandersnatch_vrfs.a ${CWD}/.lib From f371892c071d1cc54d0c9a21315c43d3a26a57c4 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 27 Sep 2024 16:50:28 +0800 Subject: [PATCH 40/89] update sh --- scripts/bandersnatch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bandersnatch.sh b/scripts/bandersnatch.sh index d16c75d5..6a8967b3 100755 --- a/scripts/bandersnatch.sh +++ b/scripts/bandersnatch.sh @@ -10,7 +10,7 @@ mkdir -p .lib cd Utils/Sources/bandersnatch || { echo "directory not found"; exit 1; } -cargo build --release --lib --target aarch64-apple-darwin +cargo build --release --lib cp target/release/libbandersnatch_vrfs.a ${CWD}/.lib From fabd87833fb24287442b96408534841057267991 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 27 Sep 2024 18:53:28 +0800 Subject: [PATCH 41/89] dev_actor --- Networking/Sources/Networking/Peer.swift | 17 +-- .../Networking/msquic/QuicServer.swift | 72 ++++++++----- .../NetworkingTests/QuicServerTest.swift | 100 ++++++++++-------- 3 files changed, 107 insertions(+), 82 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index f94b5783..ceca0194 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -47,7 +47,9 @@ public actor Peer { self.config = config self.eventBus = eventBus clients = [:] - quicServer = try QuicServer(config: config, messageHandler: self) + Task { + self.quicServer = try await QuicServer(config: config, messageHandler: self) + } } deinit { @@ -55,21 +57,23 @@ public actor Peer { client.close() } clients.removeAll() - quicServer?.close() + quicServer?.closeSync() peerLogger.trace("Peer Deinit") // Clean up resources if necessary } + private nonisolated func initQuicServer() throws {} + // Respond to a message with a specific messageID using Data func respondTo(messageID: Int64, with data: Data) async -> QuicStatus { - quicServer?.respondTo(messageID: messageID, with: data) + await quicServer?.respondTo(messageID: messageID, with: data) ?? QuicStatusCode.internalError.rawValue } // Respond to a message with a specific messageID using PeerMessage func respondTo(messageID: Int64, with message: any PeerMessage) async -> QuicStatus { let messageType = message.getMessageType() - return quicServer? + return await quicServer? .respondTo( messageID: messageID, with: message.getData(), @@ -81,10 +85,9 @@ public actor Peer { // Respond to a message with a specific messageID using PeerMessage (async throws) func respondToPeerMessage(messageID: Int64, with message: any PeerMessage) async throws { let messageType = message.getMessageType() - - try await quicServer?.respondTo( + _ = try await quicServer?.respondTo( messageID: messageID, with: message.getData(), - kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral + streamKind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral ) } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index d4b697c5..dc0fe588 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -1,4 +1,3 @@ -import Atomics import Foundation import Logging import msquic @@ -11,19 +10,19 @@ public protocol QuicServerMessageHandler: AnyObject { func didReceiveError(quicServer: QuicServer, messageID: Int64, error: QuicError) } -public final class QuicServer: @unchecked Sendable { +public actor QuicServer: @unchecked Sendable { private var api: UnsafePointer? private var registration: HQuic? private var configuration: HQuic? private var listener: QuicListener? private let config: QuicConfig private weak var messageHandler: QuicServerMessageHandler? - private var pendingMessages: AtomicDictionary + private var pendingMessages: [Int64: (QuicConnection, QuicStream)] - init(config: QuicConfig, messageHandler: QuicServerMessageHandler? = nil) throws { + init(config: QuicConfig, messageHandler: QuicServerMessageHandler? = nil) async throws { self.config = config self.messageHandler = messageHandler - pendingMessages = .init() + pendingMessages = [:] var rawPointer: UnsafeRawPointer? let status: UInt32 = MsQuicOpenVersion(2, &rawPointer) @@ -45,7 +44,9 @@ public final class QuicServer: @unchecked Sendable { } api = boundPointer registration = registrationHandle - try loadConfiguration() + try config.loadConfiguration( + api: api, registration: registration, configuration: &configuration + ) listener = try QuicListener( api: api, registration: registration, configuration: configuration, config: config, messageHandler: self @@ -53,11 +54,17 @@ public final class QuicServer: @unchecked Sendable { } deinit { - close() + closeSync() serverLogger.trace("QuicServer Deinit") } - func close() { + nonisolated func closeSync() { + Task { + await close() + } + } + + private func close() async { if listener != nil { listener?.close() listener = nil @@ -83,34 +90,49 @@ public final class QuicServer: @unchecked Sendable { if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind status = stream.send(buffer: data, kind: streamKind) - _ = pendingMessages.removeValue(forKey: messageID) + pendingMessages.removeValue(forKey: messageID) } else { - peerLogger.error("Message not found") + serverLogger.error("Message not found") } return status } // Respond to a message with a specific messageID using Data - func respondTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) async throws { - if let (_, stream) = pendingMessages[messageID] { - let streamKind = kind ?? stream.kind - _ = pendingMessages.removeValue(forKey: messageID) - let quicMessage = try await stream.send(buffer: data, kind: streamKind) - peerLogger.info("Message sent: \(quicMessage)") - } else { - peerLogger.error("Message not found") + func respondTo(messageID: Int64, with data: Data, streamKind: StreamKind? = nil) async throws + -> QuicMessage + { + guard let (_, stream) = pendingMessages[messageID] else { + serverLogger.error("Message not found") throw QuicError.messageNotFound } + + let streamKind = streamKind ?? stream.kind + pendingMessages.removeValue(forKey: messageID) + + let quicMessage = try await Task { + try await self.sendStreamData(stream: stream, data: data, kind: streamKind) + }.value + return quicMessage + } + + private nonisolated func sendStreamData(stream: QuicStream, data: Data, kind: StreamKind) + async throws -> QuicMessage + { + try await stream.send(buffer: data, kind: kind) } } -extension QuicServer: QuicListenerMessageHandler { +extension QuicServer: @preconcurrency QuicListenerMessageHandler { public func didReceiveMessage( connection: QuicConnection, stream: QuicStream, message: QuicMessage ) { switch message.type { case .received: - processPendingMessage(connection: connection, stream: stream, message: message) + Task { + await processPendingMessage( + connection: connection, stream: stream, message: message + ) + } default: break } @@ -119,20 +141,14 @@ extension QuicServer: QuicListenerMessageHandler { public func didReceiveError( connection _: QuicConnection, stream _: QuicStream, error: QuicError ) { - logger.error("Failed to receive message: \(error)") + serverLogger.error("Failed to receive message: \(error)") } private func processPendingMessage( connection: QuicConnection, stream: QuicStream, message: QuicMessage - ) { + ) async { let messageID = Int64(Date().timeIntervalSince1970 * 1000) pendingMessages[messageID] = (connection, stream) messageHandler?.didReceiveMessage(quicServer: self, messageID: messageID, message: message) } } - -extension QuicServer { - private func loadConfiguration() throws { - try config.loadConfiguration(api: api, registration: registration, configuration: &configuration) - } -} diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index d093b8f3..16099b13 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -1,47 +1,53 @@ -import Foundation -import NIO -import Testing - -@testable import Networking - -#if os(macOS) - import CoreFoundation - import Security - - final class QuicServerTests { - @Test func start() throws { - do { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - _ = try QuicServer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4568 - ), messageHandler: self - ) - try group.next().scheduleTask(in: .seconds(5)) {}.futureResult.wait() - } catch { - print("Failed to start quic server: \(error)") - } - } - } - - extension QuicServerTests: QuicServerMessageHandler { - func didReceiveMessage(quicServer: QuicServer, messageID: Int64, message: QuicMessage) { - switch message.type { - case .received: - print("Server received message with ID \(messageID): \(message)") - _ = quicServer.respondTo(messageID: messageID, with: message.data!) - case .shutdownComplete: - print("Server shutdown complete") - case .unknown: - print("Server unknown") - default: - break - } - } - - func didReceiveError(quicServer _: QuicServer, messageID _: Int64, error: QuicError) { - print("Server error: \(error)") - } - } -#endif +// import Foundation +// import NIO +// import Testing +// +// @testable import Networking +// +// #if os(macOS) +// import CoreFoundation +// import Security +// +// final class QuicServerTests { +// @Test func start() async throws { +// do { +// let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) +// let quicServer = try await QuicServer( +// config: QuicConfig( +// id: "public-key", cert: cert, key: keyFile, alpn: "sample", +// ipAddress: "127.0.0.1", port: 4568 +// ), messageHandler: self +// ) +// quicServer.closeSync() +// +// +// try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() +// } catch { +// print("Failed to start quic server: \(error)") +// } +// } +// } +// +// extension QuicServerTests: QuicServerMessageHandler { +// func didReceiveMessage(quicServer: QuicServer, messageID: Int64, message: QuicMessage) { +// switch message.type { +// case .received: +// print("Server received message with ID \(messageID): \(message)") +// Task { +// _ = await quicServer.respondTo(messageID: messageID, with: message.data!,kind: .commonEphemeral) +// +// } +// case .shutdownComplete: +// print("Server shutdown complete") +// case .unknown: +// print("Server unknown") +// default: +// break +// } +// } +// +// func didReceiveError(quicServer _: QuicServer, messageID _: Int64, error: QuicError) { +// print("Server error: \(error)") +// } +// } +// #endif From cc7d5d686a89265a2ebfad7816dab60f32797cbb Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 09:26:46 +0800 Subject: [PATCH 42/89] update quic server --- Networking/Sources/Networking/Peer.swift | 4 +--- Networking/Sources/Networking/msquic/QuicConfig.swift | 2 +- Networking/Sources/Networking/msquic/QuicServer.swift | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index ceca0194..a1b6d659 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -47,9 +47,7 @@ public actor Peer { self.config = config self.eventBus = eventBus clients = [:] - Task { - self.quicServer = try await QuicServer(config: config, messageHandler: self) - } + quicServer = try await QuicServer(config: config, messageHandler: self) } deinit { diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 485e5409..e1135f8b 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -1,7 +1,7 @@ import Foundation import msquic -public struct QuicConfig { +public struct QuicConfig: Sendable { public let id: String public let cert: String public let key: String diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index dc0fe588..0077f016 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -59,14 +59,14 @@ public actor QuicServer: @unchecked Sendable { } nonisolated func closeSync() { - Task { - await close() + Task { [weak self] in + await self?.close() // Using weak self to avoid retain cycle } } private func close() async { if listener != nil { - listener?.close() + await listener?.close() listener = nil } if configuration != nil { From b7b5c3e1d6dbf1939b0f6accabe95616df665aae Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 09:29:46 +0800 Subject: [PATCH 43/89] udpate peer --- Networking/Sources/Networking/Peer.swift | 2 -- Networking/Sources/Networking/msquic/QuicListener.swift | 2 +- Networking/Sources/Networking/msquic/QuicServer.swift | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index a1b6d659..d991d026 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -60,8 +60,6 @@ public actor Peer { // Clean up resources if necessary } - private nonisolated func initQuicServer() throws {} - // Respond to a message with a specific messageID using Data func respondTo(messageID: Int64, with data: Data) async -> QuicStatus { await quicServer?.respondTo(messageID: messageID, with: data) diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 521529c5..d11d2ab6 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -31,7 +31,7 @@ public class QuicListener { self.configuration = configuration self.config = config self.messageHandler = messageHandler - // try openListener(port: config.port, listener: &listener) + try openListener(port: config.port, listener: &listener) } private func openListener(port: UInt16, listener: inout HQuic?) throws { diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 0077f016..565c7dc8 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -66,7 +66,7 @@ public actor QuicServer: @unchecked Sendable { private func close() async { if listener != nil { - await listener?.close() + listener?.close() listener = nil } if configuration != nil { From a3dbe4ef753967ac55dfb417593a5807ede2ebf1 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 09:50:59 +0800 Subject: [PATCH 44/89] update peer --- Networking/Sources/Networking/Peer.swift | 8 +++--- .../Networking/msquic/QuicServer.swift | 25 ++++++++----------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index d991d026..95c986bf 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -189,12 +189,10 @@ extension Peer: @preconcurrency QuicClientMessageHandler { // QuicServerMessageHandler methods extension Peer: @preconcurrency QuicServerMessageHandler { - public func didReceiveMessage(quicServer _: QuicServer, messageID: Int64, message: QuicMessage) { + public func didReceiveMessage(messageID: Int64, message: QuicMessage) async { switch message.type { case .received: - Task { - await eventBus.publish(PeerMessageReceived(messageID: messageID, message: message)) - } + await eventBus.publish(PeerMessageReceived(messageID: messageID, message: message)) case .shutdownComplete: break default: @@ -202,7 +200,7 @@ extension Peer: @preconcurrency QuicServerMessageHandler { } } - public func didReceiveError(quicServer _: QuicServer, messageID: Int64, error: QuicError) { + public func didReceiveError(messageID: Int64, error: QuicError) { peerLogger.error("Failed to receive message: \(error)") Task { await eventBus.publish(PeerErrorReceived(messageID: messageID, error: error)) diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 565c7dc8..caf1655f 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -5,9 +5,9 @@ import Utils let serverLogger: Logger = .init(label: "QuicServer") -public protocol QuicServerMessageHandler: AnyObject { - func didReceiveMessage(quicServer: QuicServer, messageID: Int64, message: QuicMessage) - func didReceiveError(quicServer: QuicServer, messageID: Int64, error: QuicError) +public protocol QuicServerMessageHandler: AnyObject, Sendable { + func didReceiveMessage(messageID: Int64, message: QuicMessage) async + func didReceiveError(messageID: Int64, error: QuicError) async } public actor QuicServer: @unchecked Sendable { @@ -128,10 +128,13 @@ extension QuicServer: @preconcurrency QuicListenerMessageHandler { ) { switch message.type { case .received: - Task { - await processPendingMessage( - connection: connection, stream: stream, message: message - ) + let messageID = Int64(Date().timeIntervalSince1970 * 1000) + pendingMessages[messageID] = (connection, stream) + + // Call messageHandler safely in the actor context + Task { [weak self] in + guard let self else { return } + await messageHandler?.didReceiveMessage(messageID: messageID, message: message) } default: break @@ -143,12 +146,4 @@ extension QuicServer: @preconcurrency QuicListenerMessageHandler { ) { serverLogger.error("Failed to receive message: \(error)") } - - private func processPendingMessage( - connection: QuicConnection, stream: QuicStream, message: QuicMessage - ) async { - let messageID = Int64(Date().timeIntervalSince1970 * 1000) - pendingMessages[messageID] = (connection, stream) - messageHandler?.didReceiveMessage(quicServer: self, messageID: messageID, message: message) - } } From 68c2579ab07ad2bd0e43e24ab43d090d17160312 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 09:53:08 +0800 Subject: [PATCH 45/89] update server --- Networking/Sources/Networking/msquic/QuicServer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index caf1655f..191f094a 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -10,7 +10,7 @@ public protocol QuicServerMessageHandler: AnyObject, Sendable { func didReceiveError(messageID: Int64, error: QuicError) async } -public actor QuicServer: @unchecked Sendable { +public actor QuicServer: Sendable { private var api: UnsafePointer? private var registration: HQuic? private var configuration: HQuic? From 00445d7ea28c91e3a50afef8b3455d4120caf7f4 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 15:49:09 +0800 Subject: [PATCH 46/89] update quic client --- Networking/Sources/Networking/Peer.swift | 22 ++-- .../Networking/msquic/QuicClient.swift | 81 ++++++-------- .../Networking/msquic/QuicConnection.swift | 6 +- .../Networking/msquic/QuicListener.swift | 2 +- .../Networking/msquic/QuicServer.swift | 2 +- .../Networking/msquic/QuicStream.swift | 10 +- .../NetworkingTests/QuicClientTest.swift | 2 +- .../NetworkingTests/QuicServerTest.swift | 101 +++++++++--------- 8 files changed, 104 insertions(+), 122 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 95c986bf..68d1ceef 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -52,7 +52,7 @@ public actor Peer { deinit { for client in clients.values { - client.close() + client.closeSync() } clients.removeAll() quicServer?.closeSync() @@ -97,13 +97,13 @@ public actor Peer { } // send message to other peer - func sendMessageToPeer( + func sentMessageToPeer( message: any PeerMessage, peerAddr: NetAddr - ) throws -> QuicStatus { + ) async throws -> QuicStatus { let buffer = message.getData() let messageType = message.getMessageType() - return try sendDataToPeer(buffer, to: peerAddr, messageType: messageType) + return try await sendDataToPeer(buffer, to: peerAddr, messageType: messageType) } // send message to other peer wait for response quicMessage @@ -122,7 +122,7 @@ public actor Peer { ipAddress: peerAddr.ipAddress, port: peerAddr.port ) // Client does not exist, create a new one - let client = try QuicClient(config: config, messageHandler: self) + let client = try await QuicClient(config: config, messageHandler: self) clients[peerAddr] = client return try await client.send( message: data, @@ -132,12 +132,12 @@ public actor Peer { } private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) - throws -> QuicStatus + async throws -> QuicStatus { if let client = clients[peerAddr] { // Client already exists, use it to send the data - return try client.send( - message: data, + return try await client.send( + data: data, streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral ) } else { @@ -146,10 +146,10 @@ public actor Peer { ipAddress: peerAddr.ipAddress, port: peerAddr.port ) // Client does not exist, create a new one - let client = try QuicClient(config: config, messageHandler: self) + let client = try await QuicClient(config: config, messageHandler: self) clients[peerAddr] = client - return try client.send( - message: data, + return try await client.send( + data: data, streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral ) } diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 49050127..8ddf5691 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -7,11 +7,10 @@ let clientLogger = Logger(label: "QuicClient") public protocol QuicClientMessageHandler: AnyObject { func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) - // TODO: add error or remove it func didReceiveError(quicClient: QuicClient, error: QuicError) } -public class QuicClient: @unchecked Sendable { +public actor QuicClient: Sendable { private var api: UnsafePointer? private var registration: HQuic? private var configuration: HQuic? @@ -19,7 +18,7 @@ public class QuicClient: @unchecked Sendable { private let config: QuicConfig private weak var messageHandler: QuicClientMessageHandler? - init(config: QuicConfig, messageHandler: QuicClientMessageHandler? = nil) throws { + public init(config: QuicConfig, messageHandler: QuicClientMessageHandler? = nil) async throws { self.config = config self.messageHandler = messageHandler var rawPointer: UnsafeRawPointer? @@ -45,66 +44,62 @@ public class QuicClient: @unchecked Sendable { api = boundPointer registration = registrationHandle - try start() + try config.loadConfiguration( + api: api, registration: registration, configuration: &configuration + ) + connection = try QuicConnection( + api: api, registration: registration, configuration: configuration, messageHandler: self + ) + try connection?.start(ipAddress: config.ipAddress, port: config.port) } deinit { - close() + closeSync() clientLogger.info("QuicClient Deinit") } - private func start() throws { - try loadConfiguration() - connection = try QuicConnection( - api: api, registration: registration, configuration: configuration, messageHandler: self - ) - try connection?.start(ipAddress: config.ipAddress, port: config.port) + nonisolated func closeSync() { + Task { [weak self] in + await self?.close() // Using weak self to avoid retain cycle + } } // Asynchronous send method that waits for a QuicMessage reply - func send(message: Data) async throws -> QuicMessage { + public func send(message: Data) async throws -> QuicMessage { try await send(message: message, streamKind: .uniquePersistent) } - // send method that returns a QuicStatus - func send(message: Data, streamKind: StreamKind) throws -> QuicStatus { + // Send method that returns a QuicStatus + public func send(message: Data, streamKind: StreamKind) async throws -> QuicMessage { guard let connection else { throw QuicError.getConnectionFailed } - let sendStream: QuicStream // Check if there is an existing stream of the same kind - = - if streamKind == .uniquePersistent { - // If there is, send the message to the existing stream - try connection.createOrGetUniquePersistentStream(kind: streamKind) - } else { - // If there is not, create a new stream - try connection.createCommonEphemeralStream() - } - return sendStream.send(buffer: message, kind: streamKind) + let sendStream: QuicStream = if streamKind == .uniquePersistent { + try await connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + try await connection.createCommonEphemeralStream() + } + return try await sendStream.send(buffer: message, kind: streamKind) } - // Asynchronous send method that waits for a QuicMessage reply - func send(message: Data, streamKind: StreamKind = .uniquePersistent) async throws -> QuicMessage { + // Send method that returns a QuicStatus + public func send(data: Data, streamKind: StreamKind) async throws -> QuicStatus { guard let connection else { throw QuicError.getConnectionFailed } - let sendStream: QuicStream // Check if there is an existing stream of the same kind - = - if streamKind == .uniquePersistent { - // If there is, send the message to the existing stream - try connection.createOrGetUniquePersistentStream(kind: streamKind) - } else { - // If there is not, create a new stream - try connection.createCommonEphemeralStream() - } - return try await sendStream.send(buffer: message) + let sendStream: QuicStream = if streamKind == .uniquePersistent { + try await connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + try await connection.createCommonEphemeralStream() + } + return sendStream.send(data: data, kind: streamKind) } func getNetAddr() -> NetAddr { NetAddr(ipAddress: config.ipAddress, port: config.port) } - func close() { + public func close() async { if let connection { connection.close() self.connection = nil @@ -128,7 +123,7 @@ public class QuicClient: @unchecked Sendable { } } -extension QuicClient: QuicConnectionMessageHandler { +extension QuicClient: @preconcurrency QuicConnectionMessageHandler { public func didReceiveMessage( connection _: QuicConnection, stream _: QuicStream?, message: QuicMessage ) { @@ -143,7 +138,7 @@ extension QuicClient: QuicConnectionMessageHandler { // Use [weak self] to avoid strong reference cycle Task { [weak self] in guard let self else { return } - close() + await close() } default: @@ -157,11 +152,3 @@ extension QuicClient: QuicConnectionMessageHandler { clientLogger.error("Failed to receive message: \(error)") } } - -extension QuicClient { - private func loadConfiguration() throws { - try config.loadConfiguration( - api: api, registration: registration, configuration: &configuration - ) - } -} diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 55949c68..54fddd95 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -14,7 +14,7 @@ public protocol QuicConnectionMessageHandler: AnyObject { ) } -public class QuicConnection { +public class QuicConnection: @unchecked Sendable { private var connection: HQuic? private var api: UnsafePointer? private var registration: HQuic? @@ -106,7 +106,7 @@ public class QuicConnection { } // Creates or retrieves a unique persistent stream - func createOrGetUniquePersistentStream(kind: StreamKind) throws -> QuicStream { + func createOrGetUniquePersistentStream(kind: StreamKind) async throws -> QuicStream { if let stream = uniquePersistentStreams[kind] { return stream } @@ -116,7 +116,7 @@ public class QuicConnection { } // Creates a common ephemeral stream - func createCommonEphemeralStream() throws -> QuicStream { + func createCommonEphemeralStream() async throws -> QuicStream { let stream: QuicStream = try QuicStream(api: api, connection: connection, .commonEphemeral, messageHandler: self) commonEphemeralStreams.append(stream) return stream diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index d11d2ab6..987ad1f1 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -132,7 +132,7 @@ extension QuicListener: QuicConnectionMessageHandler { ) { switch message.type { case .shutdownComplete: - removeConnection(connection) + removeConnection(connection) // Actor-safe; called directly case .received: if let stream { messageHandler?.didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 191f094a..af4bf688 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -89,7 +89,7 @@ public actor QuicServer: Sendable { var status = QuicStatusCode.internalError.rawValue if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind - status = stream.send(buffer: data, kind: streamKind) + status = stream.send(data: data, kind: streamKind) pendingMessages.removeValue(forKey: messageID) } else { serverLogger.error("Message not found") diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 6d669775..5129c5d2 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -4,7 +4,7 @@ import msquic let streamLogger = Logger(label: "QuicStream") -public enum StreamKind { +public enum StreamKind: Sendable { case uniquePersistent case commonEphemeral case unknown @@ -15,7 +15,7 @@ public protocol QuicStreamMessageHandler: AnyObject { func didReceiveError(_ stream: QuicStream, error: QuicError) } -public class QuicStream { +public class QuicStream: @unchecked Sendable { private var stream: HQuic? private let api: UnsafePointer? private let connection: HQuic? @@ -118,10 +118,10 @@ public class QuicStream { } // Sends data over the stream and returns the status - func send(buffer: Data, kind: StreamKind? = nil) -> QuicStatus { + func send(data: Data, kind: StreamKind? = nil) -> QuicStatus { streamLogger.info("[\(String(describing: stream))] Sending data...") var status = QuicStatusCode.success.rawValue - let messageLength = buffer.count + let messageLength = data.count let sendBufferRaw = UnsafeMutableRawPointer.allocate( byteCount: MemoryLayout.size + messageLength, @@ -132,7 +132,7 @@ public class QuicStream { let bufferPointer = UnsafeMutablePointer.allocate( capacity: messageLength ) - buffer.copyBytes(to: bufferPointer, count: messageLength) + data.copyBytes(to: bufferPointer, count: messageLength) sendBuffer.pointee.Buffer = bufferPointer sendBuffer.pointee.Length = UInt32(messageLength) diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index 56ccc233..3284d636 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -13,7 +13,7 @@ import Testing let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) let cert = Bundle.module.path(forResource: "server", ofType: "cert")! let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! - let quicClient = try QuicClient( + let quicClient = try await QuicClient( config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", ipAddress: "127.0.0.1", port: 4569 diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index 16099b13..73390106 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -1,53 +1,48 @@ -// import Foundation -// import NIO -// import Testing -// -// @testable import Networking -// -// #if os(macOS) -// import CoreFoundation -// import Security -// -// final class QuicServerTests { -// @Test func start() async throws { -// do { -// let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) -// let quicServer = try await QuicServer( -// config: QuicConfig( -// id: "public-key", cert: cert, key: keyFile, alpn: "sample", -// ipAddress: "127.0.0.1", port: 4568 -// ), messageHandler: self -// ) -// quicServer.closeSync() -// -// -// try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() -// } catch { -// print("Failed to start quic server: \(error)") -// } -// } -// } -// -// extension QuicServerTests: QuicServerMessageHandler { -// func didReceiveMessage(quicServer: QuicServer, messageID: Int64, message: QuicMessage) { -// switch message.type { -// case .received: -// print("Server received message with ID \(messageID): \(message)") -// Task { -// _ = await quicServer.respondTo(messageID: messageID, with: message.data!,kind: .commonEphemeral) -// -// } -// case .shutdownComplete: -// print("Server shutdown complete") -// case .unknown: -// print("Server unknown") -// default: -// break -// } -// } -// -// func didReceiveError(quicServer _: QuicServer, messageID _: Int64, error: QuicError) { -// print("Server error: \(error)") -// } -// } -// #endif +import Foundation +import NIO +import Testing + +@testable import Networking + +#if os(macOS) + import CoreFoundation + import Security + + final class QuicServerTests { + @Test func start() async throws { + do { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let quicServer = try await QuicServer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), messageHandler: self + ) + quicServer.closeSync() + + try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() + } catch { + print("Failed to start quic server: \(error)") + } + } + } + + extension QuicServerTests: QuicServerMessageHandler { + func didReceiveMessage(messageID: Int64, message: QuicMessage) async { + switch message.type { + case .received: + print("Server received message with ID \(messageID): \(message)") + case .shutdownComplete: + print("Server shutdown complete") + case .unknown: + print("Server unknown") + default: + break + } + } + + func didReceiveError(messageID _: Int64, error: QuicError) async { + print("Server error: \(error)") + } + } +#endif From cf03d633e9b7893fb0d31b4e68d094276362316d Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 16:03:04 +0800 Subject: [PATCH 47/89] update client --- Networking/Sources/Networking/Peer.swift | 12 ++++++------ .../Sources/Networking/msquic/QuicListener.swift | 2 +- .../Sources/Networking/msquic/QuicServer.swift | 4 ++-- Networking/Tests/NetworkingTests/PeerTest.swift | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 68d1ceef..d0ce28f8 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -81,14 +81,14 @@ public actor Peer { // Respond to a message with a specific messageID using PeerMessage (async throws) func respondToPeerMessage(messageID: Int64, with message: any PeerMessage) async throws { let messageType = message.getMessageType() - _ = try await quicServer?.respondTo( + _ = try await quicServer?.respondingTo( messageID: messageID, with: message.getData(), - streamKind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral + kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral ) } - // send message to other peer - func sendMessageToPeer( + // Sends a message to another peer asynchronously + func sendMessageToPeerAsync( message: any PeerMessage, peerAddr: NetAddr ) async throws -> QuicMessage { let buffer = message.getData() @@ -96,8 +96,8 @@ public actor Peer { return try await sendDataToPeer(buffer, to: peerAddr, messageType: messageType) } - // send message to other peer - func sentMessageToPeer( + // Sends a message to another peer and returns the status + func sendMessageToPeer( message: any PeerMessage, peerAddr: NetAddr ) async throws -> QuicStatus { let buffer = message.getData() diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 987ad1f1..d11d2ab6 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -132,7 +132,7 @@ extension QuicListener: QuicConnectionMessageHandler { ) { switch message.type { case .shutdownComplete: - removeConnection(connection) // Actor-safe; called directly + removeConnection(connection) case .received: if let stream { messageHandler?.didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index af4bf688..4f0bface 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -98,7 +98,7 @@ public actor QuicServer: Sendable { } // Respond to a message with a specific messageID using Data - func respondTo(messageID: Int64, with data: Data, streamKind: StreamKind? = nil) async throws + func respondingTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) async throws -> QuicMessage { guard let (_, stream) = pendingMessages[messageID] else { @@ -106,7 +106,7 @@ public actor QuicServer: Sendable { throw QuicError.messageNotFound } - let streamKind = streamKind ?? stream.kind + let streamKind = kind ?? stream.kind pendingMessages.removeValue(forKey: messageID) let quicMessage = try await Task { diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index afa81315..61c92d24 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -121,7 +121,7 @@ import Utils Task { do { for i in 1 ... 5 { - let messageToPeer2 = try await peer1.sendMessageToPeer( + let messageToPeer2 = try await peer1.sendMessageToPeerAsync( message: Message( data: Data("Hello from Peer1 - Message \(i)".utf8) ), @@ -129,7 +129,7 @@ import Utils ) print("Peer1 sent message \(i): \(messageToPeer2)") - let messageToPeer1 = try await peer2.sendMessageToPeer( + let messageToPeer1 = try await peer2.sendMessageToPeerAsync( message: Message( data: Data("Hello from Peer2 - Message \(i)".utf8) ), From 0f31614ccef68b8b2438b3684d491364d164fb3e Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 16:47:51 +0800 Subject: [PATCH 48/89] update actor --- .../Networking/msquic/QuicListener.swift | 22 +++++++++++-------- .../Networking/msquic/QuicServer.swift | 6 ++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index d11d2ab6..48909ecd 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -6,8 +6,8 @@ import Utils let listenLogger: Logger = .init(label: "QuicListener") public protocol QuicListenerMessageHandler: AnyObject { - func didReceiveMessage(connection: QuicConnection, stream: QuicStream, message: QuicMessage) - func didReceiveError(connection: QuicConnection, stream: QuicStream, error: QuicError) + func didReceiveMessage(connection: QuicConnection, stream: QuicStream, message: QuicMessage) async + func didReceiveError(connection: QuicConnection, stream: QuicStream, error: QuicError) async } public class QuicListener { @@ -17,14 +17,14 @@ public class QuicListener { private var config: QuicConfig private var listener: HQuic? private var connections: AtomicArray = .init() - public weak var messageHandler: QuicListenerMessageHandler? + public weak var messageHandler: QuicServer? public init( api: UnsafePointer?, registration: HQuic?, configuration: HQuic?, config: QuicConfig, - messageHandler: QuicListenerMessageHandler? = nil + messageHandler: QuicServer? = nil ) throws { self.api = api self.registration = registration @@ -134,10 +134,10 @@ extension QuicListener: QuicConnectionMessageHandler { case .shutdownComplete: removeConnection(connection) case .received: - if let stream { - messageHandler?.didReceiveMessage( - connection: connection, stream: stream, message: message - ) + if let stream, let messageHandler { + Task { + await messageHandler.didReceiveMessage(connection: connection, stream: stream, message: message) + } } default: break @@ -148,7 +148,11 @@ extension QuicListener: QuicConnectionMessageHandler { connection: QuicConnection, stream: QuicStream, error: QuicError ) { listenLogger.error("Failed to receive message: \(error)") - messageHandler?.didReceiveError(connection: connection, stream: stream, error: error) + if let messageHandler { + Task { + await messageHandler.didReceiveError(connection: connection, stream: stream, error: error) + } + } } private func removeConnection(_ connection: QuicConnection) { diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 4f0bface..49770740 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -122,10 +122,10 @@ public actor QuicServer: Sendable { } } -extension QuicServer: @preconcurrency QuicListenerMessageHandler { +extension QuicServer: QuicListenerMessageHandler { public func didReceiveMessage( connection: QuicConnection, stream: QuicStream, message: QuicMessage - ) { + ) async { switch message.type { case .received: let messageID = Int64(Date().timeIntervalSince1970 * 1000) @@ -143,7 +143,7 @@ extension QuicServer: @preconcurrency QuicListenerMessageHandler { public func didReceiveError( connection _: QuicConnection, stream _: QuicStream, error: QuicError - ) { + ) async { serverLogger.error("Failed to receive message: \(error)") } } From 4b3b4e3f713d8233735603161538451ca5683490 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 29 Sep 2024 18:52:40 +0800 Subject: [PATCH 49/89] udpate peer & client --- Networking/Sources/Networking/Peer.swift | 1 + .../Networking/msquic/QuicClient.swift | 36 +++-- .../Networking/msquic/QuicConfig.swift | 2 +- .../Networking/msquic/QuicConnection.swift | 128 ++++++++++++------ .../Networking/msquic/QuicListener.swift | 26 +++- .../Networking/msquic/QuicServer.swift | 5 +- .../Networking/msquic/QuicStream.swift | 19 ++- .../Tests/NetworkingTests/PeerTest.swift | 2 +- 8 files changed, 148 insertions(+), 71 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index d0ce28f8..3ccc02a2 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -53,6 +53,7 @@ public actor Peer { deinit { for client in clients.values { client.closeSync() + peerLogger.info("client closeSync") } clients.removeAll() quicServer?.closeSync() diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 8ddf5691..456bcae5 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -55,12 +55,13 @@ public actor QuicClient: Sendable { deinit { closeSync() - clientLogger.info("QuicClient Deinit") } nonisolated func closeSync() { + clientLogger.info("client closeSync") Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle + clientLogger.info("QuicClient Deinit") } } @@ -74,11 +75,12 @@ public actor QuicClient: Sendable { guard let connection else { throw QuicError.getConnectionFailed } - let sendStream: QuicStream = if streamKind == .uniquePersistent { - try await connection.createOrGetUniquePersistentStream(kind: streamKind) - } else { - try await connection.createCommonEphemeralStream() - } + let sendStream: QuicStream = + if streamKind == .uniquePersistent { + try await connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + try await connection.createCommonEphemeralStream() + } return try await sendStream.send(buffer: message, kind: streamKind) } @@ -87,11 +89,12 @@ public actor QuicClient: Sendable { guard let connection else { throw QuicError.getConnectionFailed } - let sendStream: QuicStream = if streamKind == .uniquePersistent { - try await connection.createOrGetUniquePersistentStream(kind: streamKind) - } else { - try await connection.createCommonEphemeralStream() - } + let sendStream: QuicStream = + if streamKind == .uniquePersistent { + try await connection.createOrGetUniquePersistentStream(kind: streamKind) + } else { + try await connection.createCommonEphemeralStream() + } return sendStream.send(data: data, kind: streamKind) } @@ -101,25 +104,29 @@ public actor QuicClient: Sendable { public func close() async { if let connection { - connection.close() + await connection.close() self.connection = nil + clientLogger.info("QuicConnection close") } if let configuration { api?.pointee.ConfigurationClose(configuration) self.configuration = nil + clientLogger.info("configuration close") } if let registration { api?.pointee.RegistrationClose(registration) self.registration = nil + clientLogger.info("registration close") } if api != nil { MsQuicClose(api) api = nil + clientLogger.info("api close") } - clientLogger.debug("QuicClient Close") + clientLogger.info("QuicClient Close") } } @@ -135,6 +142,9 @@ extension QuicClient: @preconcurrency QuicConnectionMessageHandler { ) case .shutdownComplete: + clientLogger.info( + "Client[\(getNetAddr())] shutdown" + ) // Use [weak self] to avoid strong reference cycle Task { [weak self] in guard let self else { return } diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index e1135f8b..9110a492 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 3000 + settings.IdleTimeoutMs = 30000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 54fddd95..b24c2c66 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -1,7 +1,7 @@ import Foundation import Logging -import msquic import Utils +import msquic let logger = Logger(label: "QuicConnection") @@ -14,13 +14,59 @@ public protocol QuicConnectionMessageHandler: AnyObject { ) } +actor StreamManager { + private var uniquePersistentStreams: [StreamKind: QuicStream] = [:] + private var commonEphemeralStreams: [QuicStream] + + init() { + uniquePersistentStreams = [:] + commonEphemeralStreams = .init() + } + + func getUniquePersistentStream(kind: StreamKind) -> QuicStream? { + return uniquePersistentStreams[kind] + } + + func addUniquePersistentStream(kind: StreamKind, stream: QuicStream) { + uniquePersistentStreams[kind] = stream + } + + func removeUniquePersistentStream(kind: StreamKind) { + + _ = uniquePersistentStreams.removeValue(forKey: kind) + } + + func addCommonEphemeralStream(_ stream: QuicStream) { + commonEphemeralStreams.append(stream) + } + + func removeCommonEphemeralStream(_ stream: QuicStream) { + stream.close() + commonEphemeralStreams.removeAll(where: { $0 === stream }) + } + + func closeAllCommonEphemeralStreams() { + for stream in commonEphemeralStreams { + stream.close() + } + commonEphemeralStreams.removeAll() + } + + func closeAllUniquePersistentStreams() { + for stream in uniquePersistentStreams.values { + stream.close() + } + uniquePersistentStreams.removeAll() + } + +} + public class QuicConnection: @unchecked Sendable { private var connection: HQuic? private var api: UnsafePointer? private var registration: HQuic? private var configuration: HQuic? - private var uniquePersistentStreams: AtomicDictionary - private var commonEphemeralStreams: AtomicArray + private var streamManager: StreamManager private weak var messageHandler: QuicConnectionMessageHandler? private var connectionCallback: ConnectionCallback? @@ -35,8 +81,7 @@ public class QuicConnection: @unchecked Sendable { self.registration = registration self.configuration = configuration self.messageHandler = messageHandler - uniquePersistentStreams = .init() - commonEphemeralStreams = .init() + self.streamManager = StreamManager() connectionCallback = { connection, context, event in QuicConnection.connectionCallback( connection: connection, context: context, event: event @@ -44,7 +89,6 @@ public class QuicConnection: @unchecked Sendable { } try open() } - // Initializer for wrapping an existing connection init( api: UnsafePointer?, registration: HQuic?, configuration: HQuic?, @@ -55,8 +99,7 @@ public class QuicConnection: @unchecked Sendable { self.configuration = configuration self.connection = connection self.messageHandler = messageHandler - uniquePersistentStreams = .init() - commonEphemeralStreams = .init() + self.streamManager = StreamManager() connectionCallback = { connection, context, event in QuicConnection.connectionCallback( connection: connection, context: context, event: event @@ -66,9 +109,17 @@ public class QuicConnection: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { - close() - logger.trace("QuicConnection Deinit") + closeSync() } + + nonisolated func closeSync() { + Task { [weak self] in + await self?.close() // Using weak self to avoid retain cycle + logger.info("QuicConnection Deinit") + + } + } + // Sets the callback handler for the connection func setCallbackHandler() -> QuicStatus { @@ -107,31 +158,44 @@ public class QuicConnection: @unchecked Sendable { // Creates or retrieves a unique persistent stream func createOrGetUniquePersistentStream(kind: StreamKind) async throws -> QuicStream { - if let stream = uniquePersistentStreams[kind] { + if let stream = await streamManager.getUniquePersistentStream(kind: kind) { return stream } let stream = try QuicStream(api: api, connection: connection, kind, messageHandler: self) - uniquePersistentStreams[kind] = stream + await streamManager.addUniquePersistentStream(kind: kind, stream: stream) return stream } // Creates a common ephemeral stream func createCommonEphemeralStream() async throws -> QuicStream { - let stream: QuicStream = try QuicStream(api: api, connection: connection, .commonEphemeral, messageHandler: self) - commonEphemeralStreams.append(stream) + let stream: QuicStream = try QuicStream( + api: api, connection: connection, .commonEphemeral, messageHandler: self) + await streamManager.addCommonEphemeralStream(stream) return stream } // Removes a stream from the connection - func removeStream(stream: QuicStream) { + func removeStream(stream: QuicStream) async { stream.close() if stream.kind == .uniquePersistent { - _ = uniquePersistentStreams.removeValue(forKey: stream.kind) + await streamManager.removeUniquePersistentStream(kind: stream.kind) } else { - commonEphemeralStreams.removeAll(where: { $0 === stream }) + await streamManager.removeCommonEphemeralStream(stream) } } + // Closes the connection and cleans up resources + func close() async { + connectionCallback = nil + messageHandler = nil + await streamManager.closeAllCommonEphemeralStreams() + await streamManager.closeAllUniquePersistentStreams() + if connection != nil { + api?.pointee.ConnectionClose(connection) + connection = nil + } + logger.info("QuicConnection close") + } // Starts the connection with the specified IP address and port func start(ipAddress: String, port: UInt16) throws { let status = @@ -143,25 +207,6 @@ public class QuicConnection: @unchecked Sendable { throw QuicError.invalidStatus(status: status.code) } } - - // Closes the connection and cleans up resources - func close() { - connectionCallback = nil - messageHandler = nil - for stream in commonEphemeralStreams { - stream.close() - } - commonEphemeralStreams.removeAll() - for stream in uniquePersistentStreams.values { - stream.close() - } - uniquePersistentStreams.removeAll() - if connection != nil { - api?.pointee.ConnectionClose(connection) - connection = nil - } - logger.debug("QuicConnection close") - } } extension QuicConnection { @@ -221,10 +266,13 @@ extension QuicConnection { logger.info("[\(String(describing: connection))] Peer stream started") let stream = event.pointee.PEER_STREAM_STARTED.Stream let quicStream = QuicStream( - api: quicConnection.api, connection: connection, stream: stream, messageHandler: quicConnection + api: quicConnection.api, connection: connection, stream: stream, + messageHandler: quicConnection ) quicStream.setCallbackHandler() - quicConnection.commonEphemeralStreams.append(quicStream) + Task { + await quicConnection.streamManager.addCommonEphemeralStream(quicStream) + } default: break @@ -238,7 +286,9 @@ extension QuicConnection: QuicStreamMessageHandler { public func didReceiveMessage(_ stream: QuicStream, message: QuicMessage) { switch message.type { case .shutdownComplete: - removeStream(stream: stream) + Task { + await removeStream(stream: stream) + } default: break } diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 48909ecd..3251ec58 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -6,7 +6,8 @@ import Utils let listenLogger: Logger = .init(label: "QuicListener") public protocol QuicListenerMessageHandler: AnyObject { - func didReceiveMessage(connection: QuicConnection, stream: QuicStream, message: QuicMessage) async + func didReceiveMessage(connection: QuicConnection, stream: QuicStream, message: QuicMessage) + async func didReceiveError(connection: QuicConnection, stream: QuicStream, error: QuicError) async } @@ -34,6 +35,10 @@ public class QuicListener { try openListener(port: config.port, listener: &listener) } + deinit { + listenLogger.info("QuicListener Deinit") + } + private func openListener(port: UInt16, listener: inout HQuic?) throws { // Open the listener let status = @@ -76,6 +81,10 @@ public class QuicListener { } } + func getNetAddr() -> NetAddr { + NetAddr(ipAddress: config.ipAddress, port: config.port) + } + private static func serverListenerCallback( listener _: HQuic?, context: UnsafeMutableRawPointer?, event: UnsafePointer? @@ -120,7 +129,9 @@ public class QuicListener { private func closeAllConnections() { for connection in connections { - connection.close() + Task { + await connection.close() + } } connections.removeAll() } @@ -136,7 +147,9 @@ extension QuicListener: QuicConnectionMessageHandler { case .received: if let stream, let messageHandler { Task { - await messageHandler.didReceiveMessage(connection: connection, stream: stream, message: message) + await messageHandler.didReceiveMessage( + connection: connection, stream: stream, message: message + ) } } default: @@ -150,13 +163,16 @@ extension QuicListener: QuicConnectionMessageHandler { listenLogger.error("Failed to receive message: \(error)") if let messageHandler { Task { - await messageHandler.didReceiveError(connection: connection, stream: stream, error: error) + await messageHandler.didReceiveError( + connection: connection, stream: stream, error: error + ) } } } private func removeConnection(_ connection: QuicConnection) { - connection.close() + listenLogger.info("listener[\(getNetAddr())] shutdown") + connection.closeSync() connections.removeAll(where: { $0 === connection }) } } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 49770740..2df6981d 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -55,16 +55,17 @@ public actor QuicServer: Sendable { deinit { closeSync() - serverLogger.trace("QuicServer Deinit") } nonisolated func closeSync() { + serverLogger.info("server closeSync") Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle + serverLogger.info("QuicServer Deinit") } } - private func close() async { + private func close() { if listener != nil { listener?.close() listener = nil diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 5129c5d2..57613cea 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -61,7 +61,7 @@ public class QuicStream: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { close() - streamLogger.trace("QuicStream Deinit") + streamLogger.info("QuicStream Deinit") } // Opens a stream with the specified kind @@ -76,7 +76,7 @@ public class QuicStream: @unchecked Sendable { if status.isFailed { throw QuicError.invalidStatus(status: status.code) } - streamLogger.info("[\(String(describing: stream))] Stream opened") +// streamLogger.info("[\(String(describing: stream))] Stream opened") } // Starts the stream @@ -86,7 +86,7 @@ public class QuicStream: @unchecked Sendable { if status.isFailed { throw QuicError.invalidStatus(status: status.code) } - streamLogger.info("[\(String(describing: stream))] Stream started") +// streamLogger.info("[\(String(describing: stream))] Stream started") } // Closes the stream and cleans up resources @@ -96,8 +96,8 @@ public class QuicStream: @unchecked Sendable { if stream != nil { api?.pointee.StreamClose(stream) stream = nil + streamLogger.info("QuicStream close") } - streamLogger.debug("QuicStream close") } // Sets the callback handler for the stream @@ -211,13 +211,11 @@ extension QuicStream { let quicStream: QuicStream = Unmanaged.fromOpaque(context).takeUnretainedValue() var status: QuicStatus = QuicStatusCode.success.rawValue - streamLogger.info("[\(String(describing: stream))] Event: \(event.pointee.Type.rawValue)") switch event.pointee.Type { case QUIC_STREAM_EVENT_SEND_COMPLETE: if let clientContext = event.pointee.SEND_COMPLETE.ClientContext { free(clientContext) } - streamLogger.info("[\(String(describing: stream))] Data sent") case QUIC_STREAM_EVENT_RECEIVE: let bufferCount: UInt32 = event.pointee.RECEIVE.BufferCount @@ -227,9 +225,9 @@ extension QuicStream { let buffer = buffers![Int(i)] let bufferLength = Int(buffer.Length) let bufferData = Data(bytes: buffer.Buffer, count: bufferLength) - streamLogger.info( - " Data length \(bufferLength) bytes: \(String([UInt8](bufferData).map { Character(UnicodeScalar($0)) }))" - ) +// streamLogger.info( +// " Data length \(bufferLength) bytes: \(String([UInt8](bufferData).map { Character(UnicodeScalar($0)) }))" +// ) receivedData.append(bufferData) } if receivedData.count > 0 { @@ -243,7 +241,8 @@ extension QuicStream { } case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: - streamLogger.info("[\(String(describing: stream))] Peer shut down") +// streamLogger.info("[\(String(describing: stream))] Peer shut down") + break case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: streamLogger.error("[\(String(describing: stream))] Peer aborted") diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 61c92d24..07e4a6c1 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -120,7 +120,7 @@ import Utils _ = try await group.next().scheduleTask(in: .seconds(2)) { Task { do { - for i in 1 ... 5 { + for i in 1 ... 1 { let messageToPeer2 = try await peer1.sendMessageToPeerAsync( message: Message( data: Data("Hello from Peer1 - Message \(i)".utf8) From 2a69adb0d0291336c7505c798d75f77dedf37507 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 30 Sep 2024 11:01:51 +0800 Subject: [PATCH 50/89] update peer2peer --- Networking/Sources/Networking/Peer.swift | 3 -- .../Networking/msquic/QuicClient.swift | 38 +++++++------------ .../Networking/msquic/QuicConfig.swift | 2 +- .../Networking/msquic/QuicConnection.swift | 34 ++++++++--------- .../Networking/msquic/QuicListener.swift | 3 +- .../Networking/msquic/QuicServer.swift | 34 ++++++++--------- .../Networking/msquic/QuicStream.swift | 10 ++--- 7 files changed, 53 insertions(+), 71 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 3ccc02a2..b2591de7 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -53,12 +53,10 @@ public actor Peer { deinit { for client in clients.values { client.closeSync() - peerLogger.info("client closeSync") } clients.removeAll() quicServer?.closeSync() peerLogger.trace("Peer Deinit") - // Clean up resources if necessary } // Respond to a message with a specific messageID using Data @@ -170,7 +168,6 @@ extension Peer: @preconcurrency QuicClientMessageHandler { public func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) { switch message.type { case .close: - peerLogger.trace("QuicClient close") Task { [weak self] in guard let self else { return } await removeClient(with: quicClient.getNetAddr()) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 456bcae5..0d2ee910 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -58,7 +58,6 @@ public actor QuicClient: Sendable { } nonisolated func closeSync() { - clientLogger.info("client closeSync") Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle clientLogger.info("QuicClient Deinit") @@ -103,30 +102,19 @@ public actor QuicClient: Sendable { } public func close() async { - if let connection { - await connection.close() - self.connection = nil - clientLogger.info("QuicConnection close") - } - - if let configuration { - api?.pointee.ConfigurationClose(configuration) - self.configuration = nil - clientLogger.info("configuration close") - } - - if let registration { - api?.pointee.RegistrationClose(registration) - self.registration = nil - clientLogger.info("registration close") - } - - if api != nil { - MsQuicClose(api) - api = nil - clientLogger.info("api close") - } - clientLogger.info("QuicClient Close") + guard let connection else { return } + await connection.close() + self.connection = nil + guard let configuration else { return } + api?.pointee.ConfigurationClose(configuration) + self.configuration = nil + guard let registration else { return } + api?.pointee.RegistrationClose(registration) + self.registration = nil + guard let api else { return } + MsQuicClose(api) + self.api = nil + clientLogger.debug("[\(getNetAddr())] QuicClient Close") } } diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 9110a492..5c2bf3f9 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 30000 + settings.IdleTimeoutMs = 5000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index b24c2c66..3ac2d510 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -1,7 +1,7 @@ import Foundation import Logging -import Utils import msquic +import Utils let logger = Logger(label: "QuicConnection") @@ -24,7 +24,7 @@ actor StreamManager { } func getUniquePersistentStream(kind: StreamKind) -> QuicStream? { - return uniquePersistentStreams[kind] + uniquePersistentStreams[kind] } func addUniquePersistentStream(kind: StreamKind, stream: QuicStream) { @@ -32,7 +32,6 @@ actor StreamManager { } func removeUniquePersistentStream(kind: StreamKind) { - _ = uniquePersistentStreams.removeValue(forKey: kind) } @@ -44,21 +43,20 @@ actor StreamManager { stream.close() commonEphemeralStreams.removeAll(where: { $0 === stream }) } - + func closeAllCommonEphemeralStreams() { for stream in commonEphemeralStreams { stream.close() } commonEphemeralStreams.removeAll() } - + func closeAllUniquePersistentStreams() { for stream in uniquePersistentStreams.values { stream.close() } uniquePersistentStreams.removeAll() } - } public class QuicConnection: @unchecked Sendable { @@ -81,7 +79,7 @@ public class QuicConnection: @unchecked Sendable { self.registration = registration self.configuration = configuration self.messageHandler = messageHandler - self.streamManager = StreamManager() + streamManager = StreamManager() connectionCallback = { connection, context, event in QuicConnection.connectionCallback( connection: connection, context: context, event: event @@ -89,6 +87,7 @@ public class QuicConnection: @unchecked Sendable { } try open() } + // Initializer for wrapping an existing connection init( api: UnsafePointer?, registration: HQuic?, configuration: HQuic?, @@ -99,7 +98,7 @@ public class QuicConnection: @unchecked Sendable { self.configuration = configuration self.connection = connection self.messageHandler = messageHandler - self.streamManager = StreamManager() + streamManager = StreamManager() connectionCallback = { connection, context, event in QuicConnection.connectionCallback( connection: connection, context: context, event: event @@ -111,16 +110,14 @@ public class QuicConnection: @unchecked Sendable { deinit { closeSync() } - + nonisolated func closeSync() { Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle logger.info("QuicConnection Deinit") - } } - // Sets the callback handler for the connection func setCallbackHandler() -> QuicStatus { guard let api, let connection, let configuration else { @@ -169,7 +166,8 @@ public class QuicConnection: @unchecked Sendable { // Creates a common ephemeral stream func createCommonEphemeralStream() async throws -> QuicStream { let stream: QuicStream = try QuicStream( - api: api, connection: connection, .commonEphemeral, messageHandler: self) + api: api, connection: connection, .commonEphemeral, messageHandler: self + ) await streamManager.addCommonEphemeralStream(stream) return stream } @@ -186,16 +184,18 @@ public class QuicConnection: @unchecked Sendable { // Closes the connection and cleans up resources func close() async { + guard connectionCallback != nil else { return } connectionCallback = nil + guard messageHandler != nil else { return } messageHandler = nil await streamManager.closeAllCommonEphemeralStreams() await streamManager.closeAllUniquePersistentStreams() - if connection != nil { - api?.pointee.ConnectionClose(connection) - connection = nil - } - logger.info("QuicConnection close") + guard let connection else { return } + logger.info("QuicConnection ConnectionClose") + api?.pointee.ConnectionClose(connection) + self.connection = nil } + // Starts the connection with the specified IP address and port func start(ipAddress: String, port: UInt16) throws { let status = diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 3251ec58..0f6c4c7e 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -36,7 +36,7 @@ public class QuicListener { } deinit { - listenLogger.info("QuicListener Deinit") + listenLogger.trace("QuicListener Deinit") } private func openListener(port: UInt16, listener: inout HQuic?) throws { @@ -171,7 +171,6 @@ extension QuicListener: QuicConnectionMessageHandler { } private func removeConnection(_ connection: QuicConnection) { - listenLogger.info("listener[\(getNetAddr())] shutdown") connection.closeSync() connections.removeAll(where: { $0 === connection }) } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 2df6981d..831bfb5c 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -58,7 +58,6 @@ public actor QuicServer: Sendable { } nonisolated func closeSync() { - serverLogger.info("server closeSync") Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle serverLogger.info("QuicServer Deinit") @@ -66,23 +65,22 @@ public actor QuicServer: Sendable { } private func close() { - if listener != nil { - listener?.close() - listener = nil - } - if configuration != nil { - api?.pointee.ConfigurationClose(configuration) - configuration = nil - } - if registration != nil { - api?.pointee.RegistrationClose(registration) - registration = nil - } - if api != nil { - MsQuicClose(api) - api = nil - } - serverLogger.info("QuicServer Close") + guard let listener else { return } + listener.close() + self.listener = nil + serverLogger.debug("QuicListener close") + guard let configuration else { return } + api?.pointee.ConfigurationClose(configuration) + self.configuration = nil + serverLogger.debug("configuration close") + guard let registration else { return } + api?.pointee.RegistrationClose(registration) + self.registration = nil + serverLogger.debug("registration close") + guard let api else { return } + MsQuicClose(api) + self.api = nil + serverLogger.debug("QuicServer Close") } // Respond to a message with a specific messageID using Data diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 57613cea..bdd36dd1 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -91,13 +91,13 @@ public class QuicStream: @unchecked Sendable { // Closes the stream and cleans up resources func close() { + guard streamCallback != nil else { return } streamCallback = nil messageHandler = nil - if stream != nil { - api?.pointee.StreamClose(stream) - stream = nil - streamLogger.info("QuicStream close") - } + guard let stream else { return } + api?.pointee.StreamClose(stream) + self.stream = nil + streamLogger.info("QuicStream close") } // Sets the callback handler for the stream From 47911af6d60c69dd0ee5c8cfc682623ff0c3e914 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 30 Sep 2024 13:45:36 +0800 Subject: [PATCH 51/89] update peer --- Networking/Sources/Networking/Peer.swift | 15 ++++++---- .../Networking/msquic/QuicClient.swift | 30 ++++++++++++++----- .../Networking/msquic/QuicConfig.swift | 2 +- .../Networking/msquic/QuicConnection.swift | 5 ++-- .../Tests/NetworkingTests/PeerTest.swift | 14 +++++++-- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index b2591de7..230916fb 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -56,7 +56,7 @@ public actor Peer { } clients.removeAll() quicServer?.closeSync() - peerLogger.trace("Peer Deinit") + peerLogger.info("Peer Deinit") } // Respond to a message with a specific messageID using Data @@ -154,7 +154,10 @@ public actor Peer { } } - private func removeClient(with peerAddr: NetAddr) { + private func removeClient(client: QuicClient) async { + peerLogger.info("remove client") + let peerAddr = await client.getNetAddr() + await client.close() _ = clients.removeValue(forKey: peerAddr) } @@ -164,13 +167,13 @@ public actor Peer { } // QuicClientMessageHandler methods -extension Peer: @preconcurrency QuicClientMessageHandler { +extension Peer: QuicClientMessageHandler { public func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) { switch message.type { - case .close: + case .shutdownComplete: Task { [weak self] in guard let self else { return } - await removeClient(with: quicClient.getNetAddr()) + await removeClient(client: quicClient) } default: break @@ -186,7 +189,7 @@ extension Peer: @preconcurrency QuicClientMessageHandler { } // QuicServerMessageHandler methods -extension Peer: @preconcurrency QuicServerMessageHandler { +extension Peer: QuicServerMessageHandler { public func didReceiveMessage(messageID: Int64, message: QuicMessage) async { switch message.type { case .received: diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 0d2ee910..e3b7c729 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -5,9 +5,9 @@ import NIO let clientLogger = Logger(label: "QuicClient") -public protocol QuicClientMessageHandler: AnyObject { - func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) - func didReceiveError(quicClient: QuicClient, error: QuicError) +public protocol QuicClientMessageHandler: AnyObject, Sendable { + func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) async + func didReceiveError(quicClient: QuicClient, error: QuicError) async } public actor QuicClient: Sendable { @@ -16,9 +16,9 @@ public actor QuicClient: Sendable { private var configuration: HQuic? private var connection: QuicConnection? private let config: QuicConfig - private weak var messageHandler: QuicClientMessageHandler? + private weak var messageHandler: Peer? - public init(config: QuicConfig, messageHandler: QuicClientMessageHandler? = nil) async throws { + public init(config: QuicConfig, messageHandler: Peer? = nil) async throws { self.config = config self.messageHandler = messageHandler var rawPointer: UnsafeRawPointer? @@ -55,12 +55,12 @@ public actor QuicClient: Sendable { deinit { closeSync() + clientLogger.info("QuicClient Deinit") } nonisolated func closeSync() { Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle - clientLogger.info("QuicClient Deinit") } } @@ -102,19 +102,27 @@ public actor QuicClient: Sendable { } public func close() async { + clientLogger.info(" [\(getNetAddr())] client close") + guard let connection else { return } await connection.close() self.connection = nil + clientLogger.info(" [\(getNetAddr())] client connection close") + guard let configuration else { return } api?.pointee.ConfigurationClose(configuration) self.configuration = nil + clientLogger.info(" [\(getNetAddr())] client configuration close") + guard let registration else { return } api?.pointee.RegistrationClose(registration) self.registration = nil + clientLogger.info(" [\(getNetAddr())] client registration close") + guard let api else { return } MsQuicClose(api) self.api = nil - clientLogger.debug("[\(getNetAddr())] QuicClient Close") + clientLogger.info("[\(getNetAddr())] QuicClient Close") } } @@ -134,9 +142,15 @@ extension QuicClient: @preconcurrency QuicConnectionMessageHandler { "Client[\(getNetAddr())] shutdown" ) // Use [weak self] to avoid strong reference cycle +// Task { [weak self] in +// guard let self else { return } +// self.messageHandler?.didReceiveMessage(quicClient: self, message: QuicMessage.init(type: .shutdownComplete, data: null)); +// await close() +// } + // Call messageHandler safely in the actor context Task { [weak self] in guard let self else { return } - await close() + await messageHandler?.didReceiveMessage(quicClient: self, message: QuicMessage(type: .shutdownComplete, data: nil)) } default: diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 5c2bf3f9..fb057238 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 5000 + settings.IdleTimeoutMs = 10000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 3ac2d510..8de0340b 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -109,12 +109,12 @@ public class QuicConnection: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { closeSync() + logger.info("QuicConnection Deinit") } nonisolated func closeSync() { Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle - logger.info("QuicConnection Deinit") } } @@ -191,9 +191,10 @@ public class QuicConnection: @unchecked Sendable { await streamManager.closeAllCommonEphemeralStreams() await streamManager.closeAllUniquePersistentStreams() guard let connection else { return } - logger.info("QuicConnection ConnectionClose") api?.pointee.ConnectionClose(connection) self.connection = nil + logger.info("QuicConnection ConnectionClose") + } // Starts the connection with the specified IP address and port diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 07e4a6c1..eefa20a1 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -47,7 +47,7 @@ import Utils } // Example subscription to PeerMessageReceived - _ = await eventBus.subscribe(PeerMessageReceived.self) { event in + let token1 = await eventBus.subscribe(PeerMessageReceived.self) { event in print( "Received message from peer messageID: \(event.messageID), message: \(event.message)" ) @@ -58,13 +58,21 @@ import Utils } // Example subscription to PeerErrorReceived - _ = await eventBus.subscribe(PeerErrorReceived.self) { event in + let token2 = await eventBus.subscribe(PeerErrorReceived.self) { event in print( "Received error from peer messageID: \(event.messageID ?? -1), error: \(event.error)" ) } - try await group.next().scheduleTask(in: .seconds(20)) {}.futureResult.get() + _ = try await group.next().scheduleTask(in: .seconds(5)) { + Task { + await eventBus.unsubscribe(token: token1) + await eventBus.unsubscribe(token: token2) + print("eventBus unsubscribe") + } + }.futureResult.get() + + try await group.next().scheduleTask(in: .seconds(10)) {}.futureResult.get() } catch { print("Failed to start peer: \(error)") From 8d025289819bf5b08bbe899421480c902b6810ad Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 30 Sep 2024 13:53:57 +0800 Subject: [PATCH 52/89] update peer test --- Networking/Tests/NetworkingTests/PeerTest.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index afa81315..5c1420a9 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -47,7 +47,7 @@ import Utils } // Example subscription to PeerMessageReceived - _ = await eventBus.subscribe(PeerMessageReceived.self) { event in + let token1 = await eventBus.subscribe(PeerMessageReceived.self) { event in print( "Received message from peer messageID: \(event.messageID), message: \(event.message)" ) @@ -58,12 +58,18 @@ import Utils } // Example subscription to PeerErrorReceived - _ = await eventBus.subscribe(PeerErrorReceived.self) { event in + let token2 = await eventBus.subscribe(PeerErrorReceived.self) { event in print( "Received error from peer messageID: \(event.messageID ?? -1), error: \(event.error)" ) } - + _ = try await group.next().scheduleTask(in: .seconds(5)) { + Task { + await eventBus.unsubscribe(token: token1) + await eventBus.unsubscribe(token: token2) + print("eventBus unsubscribe") + } + }.futureResult.get() try await group.next().scheduleTask(in: .seconds(20)) {}.futureResult.get() } catch { From af7e1b9c74313c935cb3ca1832d4cf14fb3d9264 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 30 Sep 2024 14:35:30 +0800 Subject: [PATCH 53/89] update peer --- Networking/Sources/Networking/Peer.swift | 2 +- Networking/Sources/Networking/msquic/QuicClient.swift | 2 +- Networking/Sources/Networking/msquic/QuicConfig.swift | 2 +- Networking/Sources/Networking/msquic/QuicServer.swift | 2 +- Networking/Tests/NetworkingTests/PeerTest.swift | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index f94b5783..dd869133 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -197,7 +197,7 @@ extension Peer: @preconcurrency QuicServerMessageHandler { await eventBus.publish(PeerMessageReceived(messageID: messageID, message: message)) } case .shutdownComplete: - break + peerLogger.info("quic server shutdown") default: break } diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 49050127..cebb9c29 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -50,7 +50,7 @@ public class QuicClient: @unchecked Sendable { deinit { close() - clientLogger.info("QuicClient Deinit") + clientLogger.trace("QuicClient Deinit") } private func start() throws { diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 485e5409..7bb75237 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 3000 + settings.IdleTimeoutMs = 30000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index d4b697c5..4679ba63 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -74,7 +74,7 @@ public final class QuicServer: @unchecked Sendable { MsQuicClose(api) api = nil } - serverLogger.info("QuicServer Close") + serverLogger.trace("QuicServer Close") } // Respond to a message with a specific messageID using Data diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 5c1420a9..4d10bd43 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -70,7 +70,7 @@ import Utils print("eventBus unsubscribe") } }.futureResult.get() - try await group.next().scheduleTask(in: .seconds(20)) {}.futureResult.get() + try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() } catch { print("Failed to start peer: \(error)") From 43eed0f70021addb7030ed311a600a196f868066 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 30 Sep 2024 14:43:52 +0800 Subject: [PATCH 54/89] update quic connection --- .../Sources/Networking/msquic/QuicConnection.swift | 10 ++++------ Networking/Sources/Networking/msquic/QuicStream.swift | 8 ++++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 8de0340b..b09c3b01 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -184,17 +184,15 @@ public class QuicConnection: @unchecked Sendable { // Closes the connection and cleans up resources func close() async { - guard connectionCallback != nil else { return } connectionCallback = nil - guard messageHandler != nil else { return } messageHandler = nil await streamManager.closeAllCommonEphemeralStreams() await streamManager.closeAllUniquePersistentStreams() - guard let connection else { return } - api?.pointee.ConnectionClose(connection) - self.connection = nil + if let connection { + api?.pointee.ConnectionClose(connection) + self.connection = nil + } logger.info("QuicConnection ConnectionClose") - } // Starts the connection with the specified IP address and port diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index bdd36dd1..ac196399 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -91,12 +91,12 @@ public class QuicStream: @unchecked Sendable { // Closes the stream and cleans up resources func close() { - guard streamCallback != nil else { return } streamCallback = nil messageHandler = nil - guard let stream else { return } - api?.pointee.StreamClose(stream) - self.stream = nil + if let stream { + api?.pointee.StreamClose(stream) + self.stream = nil + } streamLogger.info("QuicStream close") } From 75598048cd814a29f359506f4be19f3b4d1bbc68 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 30 Sep 2024 16:28:24 +0800 Subject: [PATCH 55/89] update peer --- .../Networking/msquic/QuicClient.swift | 14 +++------ .../Networking/msquic/QuicConnection.swift | 31 +++++++++++-------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index e3b7c729..3f80d3d2 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -10,7 +10,7 @@ public protocol QuicClientMessageHandler: AnyObject, Sendable { func didReceiveError(quicClient: QuicClient, error: QuicError) async } -public actor QuicClient: Sendable { +public actor QuicClient: Sendable, QuicConnectionMessageHandler { private var api: UnsafePointer? private var registration: HQuic? private var configuration: HQuic? @@ -124,9 +124,7 @@ public actor QuicClient: Sendable { self.api = nil clientLogger.info("[\(getNetAddr())] QuicClient Close") } -} -extension QuicClient: @preconcurrency QuicConnectionMessageHandler { public func didReceiveMessage( connection _: QuicConnection, stream _: QuicStream?, message: QuicMessage ) { @@ -141,16 +139,12 @@ extension QuicClient: @preconcurrency QuicConnectionMessageHandler { clientLogger.info( "Client[\(getNetAddr())] shutdown" ) - // Use [weak self] to avoid strong reference cycle -// Task { [weak self] in -// guard let self else { return } -// self.messageHandler?.didReceiveMessage(quicClient: self, message: QuicMessage.init(type: .shutdownComplete, data: null)); -// await close() -// } // Call messageHandler safely in the actor context Task { [weak self] in guard let self else { return } - await messageHandler?.didReceiveMessage(quicClient: self, message: QuicMessage(type: .shutdownComplete, data: nil)) + await messageHandler?.didReceiveMessage( + quicClient: self, message: QuicMessage(type: .shutdownComplete, data: nil) + ) } default: diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index b09c3b01..b5a0d90f 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -8,10 +8,10 @@ let logger = Logger(label: "QuicConnection") public protocol QuicConnectionMessageHandler: AnyObject { func didReceiveMessage( connection: QuicConnection, stream: QuicStream?, message: QuicMessage - ) + ) async func didReceiveError( connection: QuicConnection, stream: QuicStream, error: QuicError - ) + ) async } actor StreamManager { @@ -109,7 +109,6 @@ public class QuicConnection: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { closeSync() - logger.info("QuicConnection Deinit") } nonisolated func closeSync() { @@ -192,7 +191,7 @@ public class QuicConnection: @unchecked Sendable { api?.pointee.ConnectionClose(connection) self.connection = nil } - logger.info("QuicConnection ConnectionClose") + logger.debug("QuicConnection Close") } // Starts the connection with the specified IP address and port @@ -247,12 +246,14 @@ extension QuicConnection { logger.info("[\(String(describing: connection))] Shutdown all done") if event.pointee.SHUTDOWN_COMPLETE.AppCloseInProgress == 0 { if let messageHandler = quicConnection.messageHandler { - messageHandler.didReceiveMessage( - connection: quicConnection, - stream: nil, - message: QuicMessage(type: .shutdownComplete, data: nil) - ) - quicConnection.messageHandler = nil + Task { + await messageHandler.didReceiveMessage( + connection: quicConnection, + stream: nil, + message: QuicMessage(type: .shutdownComplete, data: nil) + ) + quicConnection.messageHandler = nil + } } } @@ -291,12 +292,16 @@ extension QuicConnection: QuicStreamMessageHandler { default: break } - messageHandler?.didReceiveMessage(connection: self, stream: stream, message: message) + // Call messageHandler safely in the actor context + Task { [weak self] in + guard let self else { return } + await messageHandler?.didReceiveMessage(connection: self, stream: stream, message: message) + } } // Handles errors received from the stream - public func didReceiveError(_ stream: QuicStream, error: QuicError) { + public func didReceiveError(_: QuicStream, error: QuicError) { logger.error("Failed to receive message: \(error)") - messageHandler?.didReceiveError(connection: self, stream: stream, error: error) +// await messageHandler?.didReceiveError(connection: self, stream: stream, error: error) } } From 231dc46df7e59cc0444678121928caedc1f54963 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 30 Sep 2024 17:57:36 +0800 Subject: [PATCH 56/89] update peer --- Networking/Sources/Networking/msquic/QuicListener.swift | 4 ++-- Networking/Sources/Networking/msquic/QuicServer.swift | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 0f6c4c7e..f1a253c4 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -11,14 +11,14 @@ public protocol QuicListenerMessageHandler: AnyObject { func didReceiveError(connection: QuicConnection, stream: QuicStream, error: QuicError) async } -public class QuicListener { +public class QuicListener: @unchecked Sendable { private var api: UnsafePointer? private var registration: HQuic? private var configuration: HQuic? private var config: QuicConfig private var listener: HQuic? private var connections: AtomicArray = .init() - public weak var messageHandler: QuicServer? + public weak var messageHandler: QuicListenerMessageHandler? public init( api: UnsafePointer?, diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 831bfb5c..42b348b0 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -10,7 +10,7 @@ public protocol QuicServerMessageHandler: AnyObject, Sendable { func didReceiveError(messageID: Int64, error: QuicError) async } -public actor QuicServer: Sendable { +public actor QuicServer: Sendable, QuicListenerMessageHandler { private var api: UnsafePointer? private var registration: HQuic? private var configuration: HQuic? @@ -119,9 +119,7 @@ public actor QuicServer: Sendable { { try await stream.send(buffer: data, kind: kind) } -} -extension QuicServer: QuicListenerMessageHandler { public func didReceiveMessage( connection: QuicConnection, stream: QuicStream, message: QuicMessage ) async { From 2b6776792c9c7be5316c52f7f3e3dd5113366b4e Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 1 Oct 2024 05:31:02 +0800 Subject: [PATCH 57/89] update quic client --- Networking/Sources/Networking/Peer.swift | 2 +- Networking/Sources/Networking/msquic/QuicClient.swift | 2 +- Networking/Sources/Networking/msquic/QuicServer.swift | 2 +- Networking/Sources/Networking/msquic/QuicStream.swift | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 230916fb..195f6a68 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -56,7 +56,7 @@ public actor Peer { } clients.removeAll() quicServer?.closeSync() - peerLogger.info("Peer Deinit") + peerLogger.trace("Peer Deinit") } // Respond to a message with a specific messageID using Data diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 3f80d3d2..4373c0ff 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -55,7 +55,7 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { deinit { closeSync() - clientLogger.info("QuicClient Deinit") + clientLogger.trace("QuicClient Deinit") } nonisolated func closeSync() { diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 42b348b0..2b909ccb 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -60,7 +60,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { nonisolated func closeSync() { Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle - serverLogger.info("QuicServer Deinit") + serverLogger.trace("QuicServer Deinit") } } diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index ac196399..37864419 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -61,7 +61,7 @@ public class QuicStream: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { close() - streamLogger.info("QuicStream Deinit") + streamLogger.trace("QuicStream Deinit") } // Opens a stream with the specified kind @@ -97,7 +97,7 @@ public class QuicStream: @unchecked Sendable { api?.pointee.StreamClose(stream) self.stream = nil } - streamLogger.info("QuicStream close") + streamLogger.debug("QuicStream close") } // Sets the callback handler for the stream From 60a21358ea92457580e82a1f2dc2b5a28c1312c0 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 4 Oct 2024 15:38:14 +0800 Subject: [PATCH 58/89] update peer --- Networking/Sources/Networking/Peer.swift | 19 ++++------ .../Networking/msquic/QuicServer.swift | 4 +- .../Tests/NetworkingTests/PeerTest.swift | 37 ++++++++----------- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 195f6a68..103ef0b7 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -60,13 +60,13 @@ public actor Peer { } // Respond to a message with a specific messageID using Data - func respondTo(messageID: Int64, with data: Data) async -> QuicStatus { + func respond(to messageID: Int64, with data: Data) async -> QuicStatus { await quicServer?.respondTo(messageID: messageID, with: data) ?? QuicStatusCode.internalError.rawValue } // Respond to a message with a specific messageID using PeerMessage - func respondTo(messageID: Int64, with message: any PeerMessage) async -> QuicStatus { + func respond(to messageID: Int64, with message: any PeerMessage) async -> QuicStatus { let messageType = message.getMessageType() return await quicServer? .respondTo( @@ -78,7 +78,7 @@ public actor Peer { } // Respond to a message with a specific messageID using PeerMessage (async throws) - func respondToPeerMessage(messageID: Int64, with message: any PeerMessage) async throws { + func respond(to messageID: Int64, with message: any PeerMessage) async throws { let messageType = message.getMessageType() _ = try await quicServer?.respondingTo( messageID: messageID, with: message.getData(), @@ -87,22 +87,17 @@ public actor Peer { } // Sends a message to another peer asynchronously - func sendMessageToPeerAsync( - message: any PeerMessage, peerAddr: NetAddr - ) async throws -> QuicMessage { + func sendMessage(to peer: NetAddr, with message: any PeerMessage) async throws -> QuicMessage { let buffer = message.getData() let messageType = message.getMessageType() - return try await sendDataToPeer(buffer, to: peerAddr, messageType: messageType) + return try await sendDataToPeer(buffer, to: peer, messageType: messageType) } // Sends a message to another peer and returns the status - func sendMessageToPeer( - message: any PeerMessage, peerAddr: NetAddr - ) async throws -> QuicStatus { + func sendMessage(to peer: NetAddr, with message: any PeerMessage) async throws -> QuicStatus { let buffer = message.getData() let messageType = message.getMessageType() - - return try await sendDataToPeer(buffer, to: peerAddr, messageType: messageType) + return try await sendDataToPeer(buffer, to: peer, messageType: messageType) } // send message to other peer wait for response quicMessage diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 2b909ccb..2ae11062 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -64,7 +64,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { } } - private func close() { + private func close() async { guard let listener else { return } listener.close() self.listener = nil @@ -84,7 +84,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { } // Respond to a message with a specific messageID using Data - func respondTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) -> QuicStatus { + func respondTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) async -> QuicStatus { var status = QuicStatusCode.internalError.rawValue if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index eefa20a1..ec23d098 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -37,11 +37,11 @@ import Utils eventBus: eventBus ) do { - let quicmessage = try await peer.sendMessageToPeer( - message: Message(data: Data("Hello, World!".utf8)), - peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4569) + let message: QuicMessage = try await peer.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message(data: Data("Hello, World!".utf8)) ) - print("Peer message got: \(quicmessage)") + print("Peer message got: \(message)") } catch { print("Failed to send: \(error)") } @@ -51,8 +51,8 @@ import Utils print( "Received message from peer messageID: \(event.messageID), message: \(event.message)" ) - let status: QuicStatus = await peer.respondTo( - messageID: event.messageID, with: Message(data: event.message.data!) + let status: QuicStatus = await peer.respond( + to: event.messageID, with: Message(data: event.message.data!) ) print("Peer sent: \(status)") } @@ -107,9 +107,7 @@ import Utils print( "Peer1 received message from messageID: \(event.messageID), message: \(event.message)" ) - let status: QuicStatus = await peer1.respondTo( - messageID: event.messageID, with: Message(data: event.message.data!) - ) + let status: QuicStatus = await peer1.respond(to: event.messageID, with: event.message.data!) print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") } @@ -118,9 +116,7 @@ import Utils print( "Peer2 received message from messageID: \(event.messageID), message: \(event.message)" ) - let status: QuicStatus = await peer2.respondTo( - messageID: event.messageID, with: Message(data: event.message.data!) - ) + let status: QuicStatus = await peer2.respond(to: event.messageID, with: event.message.data!) print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") } @@ -129,19 +125,18 @@ import Utils Task { do { for i in 1 ... 1 { - let messageToPeer2 = try await peer1.sendMessageToPeerAsync( - message: Message( + let messageToPeer2: QuicMessage = try await peer1.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message( data: Data("Hello from Peer1 - Message \(i)".utf8) - ), - peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4569) + ) ) print("Peer1 sent message \(i): \(messageToPeer2)") - - let messageToPeer1 = try await peer2.sendMessageToPeerAsync( - message: Message( + let messageToPeer1: QuicMessage = try await peer2.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4568), + with: Message( data: Data("Hello from Peer2 - Message \(i)".utf8) - ), - peerAddr: NetAddr(ipAddress: "127.0.0.1", port: 4568) + ) ) print("Peer2 sent message \(i): \(messageToPeer1)") } From 2f8c4f8ff4070b40207346efdf0339894bbc707f Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 4 Oct 2024 15:54:02 +0800 Subject: [PATCH 59/89] update peer test --- Networking/Tests/NetworkingTests/PeerTest.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index ec23d098..7d669a93 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -104,8 +104,9 @@ import Utils // Subscribe to PeerMessageReceived for peer1 let token1 = await eventBus1.subscribe(PeerMessageReceived.self) { event in + print( - "Peer1 received message from messageID: \(event.messageID), message: \(event.message)" + "Peer1 received message from messageID: \(event.messageID), message: \(String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }))" ) let status: QuicStatus = await peer1.respond(to: event.messageID, with: event.message.data!) print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") @@ -114,7 +115,7 @@ import Utils // Subscribe to PeerMessageReceived for peer2 let token2 = await eventBus2.subscribe(PeerMessageReceived.self) { event in print( - "Peer2 received message from messageID: \(event.messageID), message: \(event.message)" + "Peer2 received message from messageID: \(event.messageID), message: \(String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }))" ) let status: QuicStatus = await peer2.respond(to: event.messageID, with: event.message.data!) print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") @@ -124,7 +125,7 @@ import Utils _ = try await group.next().scheduleTask(in: .seconds(2)) { Task { do { - for i in 1 ... 1 { + for i in 1 ... 5 { let messageToPeer2: QuicMessage = try await peer1.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4569), with: Message( From c1da2ff0758537113af43ebf1ca6d503c93b279a Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 4 Oct 2024 17:35:40 +0800 Subject: [PATCH 60/89] update peer --- Networking/Sources/Networking/Peer.swift | 17 +++++++----- .../Networking/msquic/QuicClient.swift | 4 +-- .../Networking/msquic/QuicConnection.swift | 7 +++-- .../Networking/msquic/QuicServer.swift | 27 +++++++++---------- .../Networking/msquic/QuicStream.swift | 16 +++++------ .../Tests/NetworkingTests/PeerTest.swift | 4 +-- 6 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 103ef0b7..3d9d7ecc 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -20,7 +20,7 @@ public protocol PeerMessage: Equatable, Sendable { extension PeerMessage { public func getMessageType() -> PeerMessageType { - .uniquePersistent + .commonEphemeral } } @@ -56,12 +56,12 @@ public actor Peer { } clients.removeAll() quicServer?.closeSync() - peerLogger.trace("Peer Deinit") + peerLogger.info("Peer Deinit") } // Respond to a message with a specific messageID using Data func respond(to messageID: Int64, with data: Data) async -> QuicStatus { - await quicServer?.respondTo(messageID: messageID, with: data) + await quicServer?.respondGetStatus(to: messageID, with: data) ?? QuicStatusCode.internalError.rawValue } @@ -69,8 +69,8 @@ public actor Peer { func respond(to messageID: Int64, with message: any PeerMessage) async -> QuicStatus { let messageType = message.getMessageType() return await quicServer? - .respondTo( - messageID: messageID, + .respondGetStatus( + to: messageID, with: message.getData(), kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral ) @@ -80,10 +80,13 @@ public actor Peer { // Respond to a message with a specific messageID using PeerMessage (async throws) func respond(to messageID: Int64, with message: any PeerMessage) async throws { let messageType = message.getMessageType() - _ = try await quicServer?.respondingTo( - messageID: messageID, with: message.getData(), + let quicMessage = try await quicServer?.respondGetMessage( + to: messageID, with: message.getData(), kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral ) + if quicMessage?.type != .received { + throw QuicError.sendFailed + } } // Sends a message to another peer asynchronously diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 4373c0ff..20f1dd6b 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -55,7 +55,7 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { deinit { closeSync() - clientLogger.trace("QuicClient Deinit") + clientLogger.info("QuicClient Deinit") } nonisolated func closeSync() { @@ -80,7 +80,7 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } else { try await connection.createCommonEphemeralStream() } - return try await sendStream.send(buffer: message, kind: streamKind) + return try await sendStream.send(data: message, kind: streamKind) } // Send method that returns a QuicStatus diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index b5a0d90f..6bfb5417 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -109,6 +109,7 @@ public class QuicConnection: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { closeSync() + logger.info("QuicConnection Deinit") } nonisolated func closeSync() { @@ -295,13 +296,15 @@ extension QuicConnection: QuicStreamMessageHandler { // Call messageHandler safely in the actor context Task { [weak self] in guard let self else { return } - await messageHandler?.didReceiveMessage(connection: self, stream: stream, message: message) + await messageHandler?.didReceiveMessage( + connection: self, stream: stream, message: message + ) } } // Handles errors received from the stream public func didReceiveError(_: QuicStream, error: QuicError) { logger.error("Failed to receive message: \(error)") -// await messageHandler?.didReceiveError(connection: self, stream: stream, error: error) + // await messageHandler?.didReceiveError(connection: self, stream: stream, error: error) } } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 2ae11062..64b08e74 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -68,28 +68,30 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { guard let listener else { return } listener.close() self.listener = nil - serverLogger.debug("QuicListener close") + serverLogger.info("QuicListener close") guard let configuration else { return } api?.pointee.ConfigurationClose(configuration) self.configuration = nil - serverLogger.debug("configuration close") + serverLogger.info("configuration close") guard let registration else { return } api?.pointee.RegistrationClose(registration) self.registration = nil - serverLogger.debug("registration close") + serverLogger.info("registration close") guard let api else { return } MsQuicClose(api) self.api = nil - serverLogger.debug("QuicServer Close") + serverLogger.info("QuicServer Close") } // Respond to a message with a specific messageID using Data - func respondTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) async -> QuicStatus { + func respondGetStatus(to messageID: Int64, with data: Data, kind: StreamKind? = nil) async + -> QuicStatus + { var status = QuicStatusCode.internalError.rawValue if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind - status = stream.send(data: data, kind: streamKind) pendingMessages.removeValue(forKey: messageID) + status = stream.send(data: data, kind: streamKind) } else { serverLogger.error("Message not found") } @@ -97,7 +99,8 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { } // Respond to a message with a specific messageID using Data - func respondingTo(messageID: Int64, with data: Data, kind: StreamKind? = nil) async throws + func respondGetMessage(to messageID: Int64, with data: Data, kind: StreamKind? = nil) + async throws -> QuicMessage { guard let (_, stream) = pendingMessages[messageID] else { @@ -107,17 +110,13 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { let streamKind = kind ?? stream.kind pendingMessages.removeValue(forKey: messageID) - - let quicMessage = try await Task { - try await self.sendStreamData(stream: stream, data: data, kind: streamKind) - }.value - return quicMessage + return try await send(stream: stream, with: data, kind: streamKind) } - private nonisolated func sendStreamData(stream: QuicStream, data: Data, kind: StreamKind) + private func send(stream: QuicStream, with data: Data, kind: StreamKind) async throws -> QuicMessage { - try await stream.send(buffer: data, kind: kind) + try await stream.send(data: data, kind: kind) } public func didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 37864419..1c464a13 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -44,13 +44,14 @@ public class QuicStream: @unchecked Sendable { // Initializer for wrapping an existing stream init( api: UnsafePointer?, connection: HQuic?, stream: HQuic?, + _ streamKind: StreamKind = .commonEphemeral, messageHandler: QuicStreamMessageHandler? = nil ) { self.api = api self.connection = connection self.messageHandler = messageHandler self.stream = stream - kind = .commonEphemeral + kind = streamKind streamCallback = { stream, context, event in QuicStream.streamCallback( stream: stream, context: context, event: event @@ -61,7 +62,7 @@ public class QuicStream: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { close() - streamLogger.trace("QuicStream Deinit") + streamLogger.info("QuicStream Deinit") } // Opens a stream with the specified kind @@ -76,7 +77,6 @@ public class QuicStream: @unchecked Sendable { if status.isFailed { throw QuicError.invalidStatus(status: status.code) } -// streamLogger.info("[\(String(describing: stream))] Stream opened") } // Starts the stream @@ -86,7 +86,7 @@ public class QuicStream: @unchecked Sendable { if status.isFailed { throw QuicError.invalidStatus(status: status.code) } -// streamLogger.info("[\(String(describing: stream))] Stream started") + streamLogger.info("[\(String(describing: stream))] Stream started") } // Closes the stream and cleans up resources @@ -97,7 +97,7 @@ public class QuicStream: @unchecked Sendable { api?.pointee.StreamClose(stream) self.stream = nil } - streamLogger.debug("QuicStream close") + streamLogger.info("QuicStream close") } // Sets the callback handler for the stream @@ -154,10 +154,10 @@ public class QuicStream: @unchecked Sendable { } // Sends data over the stream asynchronously and waits for the response - func send(buffer: Data, kind: StreamKind? = nil) async throws -> QuicMessage { + func send(data: Data, kind: StreamKind? = nil) async throws -> QuicMessage { streamLogger.info("[\(String(describing: stream))] Sending data...") var status = QuicStatusCode.success.rawValue - let messageLength = buffer.count + let messageLength = data.count let sendBufferRaw = UnsafeMutableRawPointer.allocate( byteCount: MemoryLayout.size + messageLength, @@ -168,7 +168,7 @@ public class QuicStream: @unchecked Sendable { let bufferPointer = UnsafeMutablePointer.allocate( capacity: messageLength ) - buffer.copyBytes(to: bufferPointer, count: messageLength) + data.copyBytes(to: bufferPointer, count: messageLength) sendBuffer.pointee.Buffer = bufferPointer sendBuffer.pointee.Length = UInt32(messageLength) diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 7d669a93..2687fae5 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -125,7 +125,7 @@ import Utils _ = try await group.next().scheduleTask(in: .seconds(2)) { Task { do { - for i in 1 ... 5 { + for i in 1 ... 10 { let messageToPeer2: QuicMessage = try await peer1.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4569), with: Message( @@ -147,7 +147,7 @@ import Utils } }.futureResult.get() - _ = try await group.next().scheduleTask(in: .seconds(5)) { + _ = try await group.next().scheduleTask(in: .seconds(10)) { Task { await eventBus1.unsubscribe(token: token1) await eventBus2.unsubscribe(token: token2) From 188c6e451678408d38864cce60cd47360423b2f0 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Fri, 4 Oct 2024 19:44:34 +0800 Subject: [PATCH 61/89] update peer --- Networking/Sources/Networking/Peer.swift | 6 +-- .../Networking/msquic/QuicClient.swift | 10 ++-- .../Networking/msquic/QuicConnection.swift | 2 +- .../Networking/msquic/QuicListener.swift | 49 +++++++++++++------ .../Networking/msquic/QuicServer.swift | 11 ++--- 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 3d9d7ecc..0adf5993 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -20,7 +20,7 @@ public protocol PeerMessage: Equatable, Sendable { extension PeerMessage { public func getMessageType() -> PeerMessageType { - .commonEphemeral + .uniquePersistent } } @@ -56,12 +56,12 @@ public actor Peer { } clients.removeAll() quicServer?.closeSync() - peerLogger.info("Peer Deinit") + peerLogger.debug("Peer Deinit") } // Respond to a message with a specific messageID using Data func respond(to messageID: Int64, with data: Data) async -> QuicStatus { - await quicServer?.respondGetStatus(to: messageID, with: data) + await quicServer?.respondGetStatus(to: messageID, with: data, kind: .uniquePersistent) ?? QuicStatusCode.internalError.rawValue } diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 20f1dd6b..13275d18 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -102,27 +102,27 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } public func close() async { - clientLogger.info(" [\(getNetAddr())] client close") + clientLogger.debug(" [\(getNetAddr())] client close") guard let connection else { return } await connection.close() self.connection = nil - clientLogger.info(" [\(getNetAddr())] client connection close") + clientLogger.debug(" [\(getNetAddr())] client connection close") guard let configuration else { return } api?.pointee.ConfigurationClose(configuration) self.configuration = nil - clientLogger.info(" [\(getNetAddr())] client configuration close") + clientLogger.debug(" [\(getNetAddr())] client configuration close") guard let registration else { return } api?.pointee.RegistrationClose(registration) self.registration = nil - clientLogger.info(" [\(getNetAddr())] client registration close") + clientLogger.debug(" [\(getNetAddr())] client registration close") guard let api else { return } MsQuicClose(api) self.api = nil - clientLogger.info("[\(getNetAddr())] QuicClient Close") + clientLogger.debug("[\(getNetAddr())] QuicClient Close") } public func didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 6bfb5417..88b8a888 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -109,7 +109,7 @@ public class QuicConnection: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { closeSync() - logger.info("QuicConnection Deinit") + logger.trace("QuicConnection Deinit") } nonisolated func closeSync() { diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index f1a253c4..2f5c0a7d 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -5,6 +5,30 @@ import Utils let listenLogger: Logger = .init(label: "QuicListener") +actor ConnectionsManager { + private var connections: [QuicConnection] = [] + + func add(_ connection: QuicConnection) { + connections.append(connection) + } + + func remove(_ connection: QuicConnection) async { + await connection.close() + connections.removeAll(where: { $0 === connection }) + } + + func all() -> [QuicConnection] { + connections + } + + func removeAll() async { + for connection in connections { + await connection.close() + } + connections.removeAll() + } +} + public protocol QuicListenerMessageHandler: AnyObject { func didReceiveMessage(connection: QuicConnection, stream: QuicStream, message: QuicMessage) async @@ -17,8 +41,8 @@ public class QuicListener: @unchecked Sendable { private var configuration: HQuic? private var config: QuicConfig private var listener: HQuic? - private var connections: AtomicArray = .init() public weak var messageHandler: QuicListenerMessageHandler? + private let connectionsManager: ConnectionsManager public init( api: UnsafePointer?, @@ -32,6 +56,7 @@ public class QuicListener: @unchecked Sendable { self.configuration = configuration self.config = config self.messageHandler = messageHandler + connectionsManager = .init() try openListener(port: config.port, listener: &listener) } @@ -110,7 +135,9 @@ public class QuicListener: @unchecked Sendable { connection: connection, messageHandler: listener ) - listener.connections.append(quicConnection) + Task { + await listener.connectionsManager.add(quicConnection) + } status = quicConnection.setCallbackHandler() default: @@ -119,21 +146,12 @@ public class QuicListener: @unchecked Sendable { return status } - public func close() { + public func close() async { if let listener { api?.pointee.ListenerClose(listener) self.listener = nil } - closeAllConnections() - } - - private func closeAllConnections() { - for connection in connections { - Task { - await connection.close() - } - } - connections.removeAll() + await connectionsManager.removeAll() } } @@ -171,7 +189,8 @@ extension QuicListener: QuicConnectionMessageHandler { } private func removeConnection(_ connection: QuicConnection) { - connection.closeSync() - connections.removeAll(where: { $0 === connection }) + Task { + await connectionsManager.remove(connection) + } } } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 64b08e74..24b432a9 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -66,21 +66,21 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { private func close() async { guard let listener else { return } - listener.close() + await listener.close() self.listener = nil - serverLogger.info("QuicListener close") + serverLogger.debug("QuicListener close") guard let configuration else { return } api?.pointee.ConfigurationClose(configuration) self.configuration = nil - serverLogger.info("configuration close") + serverLogger.debug("configuration close") guard let registration else { return } api?.pointee.RegistrationClose(registration) self.registration = nil - serverLogger.info("registration close") + serverLogger.debug("registration close") guard let api else { return } MsQuicClose(api) self.api = nil - serverLogger.info("QuicServer Close") + serverLogger.debug("QuicServer Close") } // Respond to a message with a specific messageID using Data @@ -104,7 +104,6 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { -> QuicMessage { guard let (_, stream) = pendingMessages[messageID] else { - serverLogger.error("Message not found") throw QuicError.messageNotFound } From 07b055b3d8f72aadc4949abf073efca65f17483f Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sat, 5 Oct 2024 17:55:31 +0800 Subject: [PATCH 62/89] update peer --- Networking/Sources/Networking/Peer.swift | 4 +- .../Networking/msquic/QuicClient.swift | 2 +- .../Networking/msquic/QuicConnection.swift | 75 +++++++++++-------- .../Networking/msquic/QuicMessage.swift | 1 + .../Networking/msquic/QuicServer.swift | 5 +- .../Networking/msquic/QuicStream.swift | 36 ++++++--- .../Tests/NetworkingTests/PeerTest.swift | 40 +++++++++- 7 files changed, 111 insertions(+), 52 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 0adf5993..e808a43f 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -56,12 +56,12 @@ public actor Peer { } clients.removeAll() quicServer?.closeSync() - peerLogger.debug("Peer Deinit") + peerLogger.info("Peer Deinit") } // Respond to a message with a specific messageID using Data func respond(to messageID: Int64, with data: Data) async -> QuicStatus { - await quicServer?.respondGetStatus(to: messageID, with: data, kind: .uniquePersistent) + await quicServer?.respondGetStatus(to: messageID, with: data) ?? QuicStatusCode.internalError.rawValue } diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 13275d18..5e7a20a8 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -94,7 +94,7 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } else { try await connection.createCommonEphemeralStream() } - return sendStream.send(data: data, kind: streamKind) + return sendStream.respond(with: data, kind: streamKind) } func getNetAddr() -> NetAddr { diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 88b8a888..f08807e9 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -15,47 +15,53 @@ public protocol QuicConnectionMessageHandler: AnyObject { } actor StreamManager { - private var uniquePersistentStreams: [StreamKind: QuicStream] = [:] - private var commonEphemeralStreams: [QuicStream] + private var uniqueStreams: [StreamKind: QuicStream] = [:] + private var commonStreams: [QuicStream] init() { - uniquePersistentStreams = [:] - commonEphemeralStreams = .init() + uniqueStreams = [:] + commonStreams = .init() } - func getUniquePersistentStream(kind: StreamKind) -> QuicStream? { - uniquePersistentStreams[kind] + func getUniqueStream(kind: StreamKind) -> QuicStream? { + uniqueStreams[kind] } - func addUniquePersistentStream(kind: StreamKind, stream: QuicStream) { - uniquePersistentStreams[kind] = stream + func addUniqueStream(kind: StreamKind, stream: QuicStream) { + uniqueStreams[kind] = stream } - func removeUniquePersistentStream(kind: StreamKind) { - _ = uniquePersistentStreams.removeValue(forKey: kind) + func removeUniqueStream(kind: StreamKind) { + _ = uniqueStreams.removeValue(forKey: kind) } - func addCommonEphemeralStream(_ stream: QuicStream) { - commonEphemeralStreams.append(stream) + func addCommonStream(_ stream: QuicStream) { + commonStreams.append(stream) } - func removeCommonEphemeralStream(_ stream: QuicStream) { - stream.close() - commonEphemeralStreams.removeAll(where: { $0 === stream }) + func removeCommonStream(_ stream: QuicStream) { + commonStreams.removeAll(where: { $0 === stream }) + } + + func changeTypeToCommon(_ stream: QuicStream) { + stream.kind = .commonEphemeral + removeUniqueStream(kind: stream.kind) + removeCommonStream(stream) + addCommonStream(stream) } - func closeAllCommonEphemeralStreams() { - for stream in commonEphemeralStreams { + func closeAllCommonStreams() { + for stream in commonStreams { stream.close() } - commonEphemeralStreams.removeAll() + commonStreams.removeAll() } - func closeAllUniquePersistentStreams() { - for stream in uniquePersistentStreams.values { + func closeAllUniqueStreams() { + for stream in uniqueStreams.values { stream.close() } - uniquePersistentStreams.removeAll() + uniqueStreams.removeAll() } } @@ -109,7 +115,7 @@ public class QuicConnection: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { closeSync() - logger.trace("QuicConnection Deinit") + logger.info("QuicConnection Deinit") } nonisolated func closeSync() { @@ -155,11 +161,11 @@ public class QuicConnection: @unchecked Sendable { // Creates or retrieves a unique persistent stream func createOrGetUniquePersistentStream(kind: StreamKind) async throws -> QuicStream { - if let stream = await streamManager.getUniquePersistentStream(kind: kind) { + if let stream = await streamManager.getUniqueStream(kind: kind) { return stream } let stream = try QuicStream(api: api, connection: connection, kind, messageHandler: self) - await streamManager.addUniquePersistentStream(kind: kind, stream: stream) + await streamManager.addUniqueStream(kind: kind, stream: stream) return stream } @@ -168,7 +174,7 @@ public class QuicConnection: @unchecked Sendable { let stream: QuicStream = try QuicStream( api: api, connection: connection, .commonEphemeral, messageHandler: self ) - await streamManager.addCommonEphemeralStream(stream) + await streamManager.addCommonStream(stream) return stream } @@ -176,9 +182,9 @@ public class QuicConnection: @unchecked Sendable { func removeStream(stream: QuicStream) async { stream.close() if stream.kind == .uniquePersistent { - await streamManager.removeUniquePersistentStream(kind: stream.kind) + await streamManager.removeUniqueStream(kind: stream.kind) } else { - await streamManager.removeCommonEphemeralStream(stream) + await streamManager.removeCommonStream(stream) } } @@ -186,8 +192,8 @@ public class QuicConnection: @unchecked Sendable { func close() async { connectionCallback = nil messageHandler = nil - await streamManager.closeAllCommonEphemeralStreams() - await streamManager.closeAllUniquePersistentStreams() + await streamManager.closeAllCommonStreams() + await streamManager.closeAllUniqueStreams() if let connection { api?.pointee.ConnectionClose(connection) self.connection = nil @@ -223,7 +229,7 @@ extension QuicConnection { let status: QuicStatus = QuicStatusCode.success.rawValue switch event.pointee.Type { case QUIC_CONNECTION_EVENT_CONNECTED: - logger.info("[\(String(describing: connection))] Connected") + logger.debug("[\(String(describing: connection))] Connected") case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT: if event.pointee.SHUTDOWN_INITIATED_BY_TRANSPORT.Status @@ -272,7 +278,7 @@ extension QuicConnection { ) quicStream.setCallbackHandler() Task { - await quicConnection.streamManager.addCommonEphemeralStream(quicStream) + await quicConnection.streamManager.addCommonStream(quicStream) } default: @@ -290,6 +296,13 @@ extension QuicConnection: QuicStreamMessageHandler { Task { await removeStream(stream: stream) } + case .sendShutdown: + Task { + if stream.kind == .uniquePersistent { + await streamManager.changeTypeToCommon(stream) + logger.info("changeStreamType") + } + } default: break } diff --git a/Networking/Sources/Networking/msquic/QuicMessage.swift b/Networking/Sources/Networking/msquic/QuicMessage.swift index 3c4a2f94..a3133a58 100644 --- a/Networking/Sources/Networking/msquic/QuicMessage.swift +++ b/Networking/Sources/Networking/msquic/QuicMessage.swift @@ -6,6 +6,7 @@ enum QuicMessageType: String, Codable { case close case connected case shutdownComplete + case sendShutdown } public struct QuicMessage: Sendable, Equatable, Codable { diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 24b432a9..0370932b 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -60,7 +60,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { nonisolated func closeSync() { Task { [weak self] in await self?.close() // Using weak self to avoid retain cycle - serverLogger.trace("QuicServer Deinit") + serverLogger.info("QuicServer Deinit") } } @@ -91,7 +91,8 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind pendingMessages.removeValue(forKey: messageID) - status = stream.send(data: data, kind: streamKind) + + status = stream.respond(with: data, kind: streamKind) } else { serverLogger.error("Message not found") } diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 1c464a13..9d873f99 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -19,7 +19,7 @@ public class QuicStream: @unchecked Sendable { private var stream: HQuic? private let api: UnsafePointer? private let connection: HQuic? - public let kind: StreamKind + public var kind: StreamKind private weak var messageHandler: QuicStreamMessageHandler? private var streamCallback: StreamCallback? private var sendCompletion: CheckedContinuation? @@ -44,7 +44,7 @@ public class QuicStream: @unchecked Sendable { // Initializer for wrapping an existing stream init( api: UnsafePointer?, connection: HQuic?, stream: HQuic?, - _ streamKind: StreamKind = .commonEphemeral, + _ streamKind: StreamKind = .uniquePersistent, messageHandler: QuicStreamMessageHandler? = nil ) { self.api = api @@ -117,9 +117,8 @@ public class QuicStream: @unchecked Sendable { ) } - // Sends data over the stream and returns the status - func send(data: Data, kind: StreamKind? = nil) -> QuicStatus { - streamLogger.info("[\(String(describing: stream))] Sending data...") + func respond(with data: Data, kind: StreamKind? = nil) -> QuicStatus { + streamLogger.info("[\(String(describing: stream))] Respond data...") var status = QuicStatusCode.success.rawValue let messageLength = data.count @@ -140,7 +139,10 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN - + streamLogger + .info( + "[\(String(describing: stream))] flags \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")" + ) status = (api?.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw)).status if status.isFailed { streamLogger.error("StreamSend failed, \(status)!") @@ -176,6 +178,10 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN + streamLogger + .info( + "[\(String(describing: stream))] flags \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")" + ) return try await withCheckedThrowingContinuation { [weak self] continuation in guard let self else { @@ -225,9 +231,6 @@ extension QuicStream { let buffer = buffers![Int(i)] let bufferLength = Int(buffer.Length) let bufferData = Data(bytes: buffer.Buffer, count: bufferLength) -// streamLogger.info( -// " Data length \(bufferLength) bytes: \(String([UInt8](bufferData).map { Character(UnicodeScalar($0)) }))" -// ) receivedData.append(bufferData) } if receivedData.count > 0 { @@ -241,11 +244,20 @@ extension QuicStream { } case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: -// streamLogger.info("[\(String(describing: stream))] Peer shut down") - break + streamLogger.info("[\(String(describing: stream))] Peer send shutdown") + + quicStream.messageHandler?.didReceiveMessage( + quicStream, message: QuicMessage(type: .sendShutdown, data: nil) + ) + if quicStream.kind == .uniquePersistent { + quicStream.kind = .commonEphemeral + streamLogger.warning( + "[\(String(describing: stream))] Changing stream to commonEphemeral" + ) + } case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: - streamLogger.error("[\(String(describing: stream))] Peer aborted") + streamLogger.error("[\(String(describing: stream))] Peer send aborted") status = (quicStream.api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)) .status diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 2687fae5..926f223a 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -11,8 +11,19 @@ import Utils struct Message: PeerMessage { public let data: Data + public let type: PeerMessageType? public init(data: Data) { self.data = data + type = nil + } + + public init(data: Data, type: PeerMessageType) { + self.data = data + self.type = type + } + + public func getMessageType() -> PeerMessageType { + type ?? .uniquePersistent } public func getData() -> Data { @@ -125,18 +136,39 @@ import Utils _ = try await group.next().scheduleTask(in: .seconds(2)) { Task { do { - for i in 1 ... 10 { + for i in 1 ... 5 { + let messageToPeer2: QuicMessage = try await peer1.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message( + data: Data("Hello from Peer1 - Message \(i)".utf8), + type: PeerMessageType.commonEphemeral + ) + ) + print("Peer1 got message: \(String([UInt8](messageToPeer2.data!).map { Character(UnicodeScalar($0)) }))") + let messageToPeer1: QuicMessage = try await peer2.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4568), + with: Message( + data: Data("Hello from Peer2 - Message \(i)".utf8), + type: PeerMessageType.commonEphemeral + ) + ) + print("Peer2 got message: \(String([UInt8](messageToPeer1.data!).map { Character(UnicodeScalar($0)) }))") + } + + for i in 6 ... 10 { let messageToPeer2: QuicMessage = try await peer1.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4569), with: Message( - data: Data("Hello from Peer1 - Message \(i)".utf8) + data: Data("Hello from Peer1 - Message \(i)".utf8), + type: PeerMessageType.uniquePersistent ) ) print("Peer1 sent message \(i): \(messageToPeer2)") let messageToPeer1: QuicMessage = try await peer2.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4568), with: Message( - data: Data("Hello from Peer2 - Message \(i)".utf8) + data: Data("Hello from Peer2 - Message \(i)".utf8), + type: PeerMessageType.uniquePersistent ) ) print("Peer2 sent message \(i): \(messageToPeer1)") @@ -147,7 +179,7 @@ import Utils } }.futureResult.get() - _ = try await group.next().scheduleTask(in: .seconds(10)) { + _ = try await group.next().scheduleTask(in: .seconds(100)) { Task { await eventBus1.unsubscribe(token: token1) await eventBus2.unsubscribe(token: token2) From 337b79977c7704db8cbddbf4fc3f7da0ac3be994 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 6 Oct 2024 15:07:24 +0800 Subject: [PATCH 63/89] update peer --- Networking/Sources/Networking/Peer.swift | 16 +++++++++--- .../Networking/msquic/QuicClient.swift | 17 ++++--------- .../Networking/msquic/QuicConfig.swift | 12 ++++----- .../Networking/msquic/QuicConnection.swift | 9 +------ .../Networking/msquic/QuicMessage.swift | 2 +- .../Networking/msquic/QuicServer.swift | 11 ++------ .../Networking/msquic/QuicStatus.swift | 4 +++ .../Networking/msquic/QuicStream.swift | 25 +++++++++---------- .../Tests/NetworkingTests/PeerTest.swift | 12 +++++---- .../NetworkingTests/QuicServerTest.swift | 2 +- 10 files changed, 52 insertions(+), 58 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index e808a43f..23748840 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -51,12 +51,22 @@ public actor Peer { } deinit { + closeSync() + peerLogger.info("Peer Deinit") + } + + nonisolated func closeSync() { + Task { [weak self] in + await self?.close() // Using weak self to avoid retain cycle + } + } + + private func close() async { for client in clients.values { - client.closeSync() + await client.close() } clients.removeAll() - quicServer?.closeSync() - peerLogger.info("Peer Deinit") + await quicServer?.close() } // Respond to a message with a specific messageID using Data diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 5e7a20a8..7815214a 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -54,16 +54,9 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } deinit { - closeSync() clientLogger.info("QuicClient Deinit") } - nonisolated func closeSync() { - Task { [weak self] in - await self?.close() // Using weak self to avoid retain cycle - } - } - // Asynchronous send method that waits for a QuicMessage reply public func send(message: Data) async throws -> QuicMessage { try await send(message: message, streamKind: .uniquePersistent) @@ -102,27 +95,27 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } public func close() async { - clientLogger.debug(" [\(getNetAddr())] client close") + clientLogger.info(" [\(getNetAddr())] client close") guard let connection else { return } await connection.close() self.connection = nil - clientLogger.debug(" [\(getNetAddr())] client connection close") + clientLogger.info(" [\(getNetAddr())] client connection close") guard let configuration else { return } api?.pointee.ConfigurationClose(configuration) self.configuration = nil - clientLogger.debug(" [\(getNetAddr())] client configuration close") + clientLogger.info(" [\(getNetAddr())] client configuration close") guard let registration else { return } api?.pointee.RegistrationClose(registration) self.registration = nil - clientLogger.debug(" [\(getNetAddr())] client registration close") + clientLogger.info(" [\(getNetAddr())] client registration close") guard let api else { return } MsQuicClose(api) self.api = nil - clientLogger.debug("[\(getNetAddr())] QuicClient Close") + clientLogger.info("[\(getNetAddr())] QuicClient Close") } public func didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index fb057238..755cd647 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 10000 + settings.IdleTimeoutMs = 20000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 @@ -38,11 +38,11 @@ public struct QuicConfig: Sendable { QuicCredentialConfig.__Unnamed_union___Anonymous_field2( CertificateFile: certFilePointer ), - Principal: nil, // Not needed in this context - Reserved: nil, // Not needed in this context - AsyncHandler: nil, // Not needed in this context - AllowedCipherSuites: QUIC_ALLOWED_CIPHER_SUITE_NONE, // Default value - CaCertificateFile: nil // Not needed in this context + Principal: nil, + Reserved: nil, + AsyncHandler: nil, + AllowedCipherSuites: QUIC_ALLOWED_CIPHER_SUITE_NONE, + CaCertificateFile: nil ) // Convert ALPN to data buffer diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index f08807e9..ba845ae0 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -114,16 +114,9 @@ public class QuicConnection: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { - closeSync() logger.info("QuicConnection Deinit") } - nonisolated func closeSync() { - Task { [weak self] in - await self?.close() // Using weak self to avoid retain cycle - } - } - // Sets the callback handler for the connection func setCallbackHandler() -> QuicStatus { guard let api, let connection, let configuration else { @@ -296,7 +289,7 @@ extension QuicConnection: QuicStreamMessageHandler { Task { await removeStream(stream: stream) } - case .sendShutdown: + case .changeStreamType: Task { if stream.kind == .uniquePersistent { await streamManager.changeTypeToCommon(stream) diff --git a/Networking/Sources/Networking/msquic/QuicMessage.swift b/Networking/Sources/Networking/msquic/QuicMessage.swift index a3133a58..1248c862 100644 --- a/Networking/Sources/Networking/msquic/QuicMessage.swift +++ b/Networking/Sources/Networking/msquic/QuicMessage.swift @@ -6,7 +6,7 @@ enum QuicMessageType: String, Codable { case close case connected case shutdownComplete - case sendShutdown + case changeStreamType } public struct QuicMessage: Sendable, Equatable, Codable { diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 0370932b..7dd7fac5 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -54,17 +54,10 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { } deinit { - closeSync() + serverLogger.info("QuicServer Deinit") } - nonisolated func closeSync() { - Task { [weak self] in - await self?.close() // Using weak self to avoid retain cycle - serverLogger.info("QuicServer Deinit") - } - } - - private func close() async { + public func close() async { guard let listener else { return } await listener.close() self.listener = nil diff --git a/Networking/Sources/Networking/msquic/QuicStatus.swift b/Networking/Sources/Networking/msquic/QuicStatus.swift index 1dcbe923..55dd0326 100644 --- a/Networking/Sources/Networking/msquic/QuicStatus.swift +++ b/Networking/Sources/Networking/msquic/QuicStatus.swift @@ -20,6 +20,10 @@ extension QuicStatus { Int32(bitPattern: self) <= 0 } + static var pending: UInt32 { + UInt32(bitPattern: -2) + } + init(_ value: UInt32?) { guard let value else { self = QuicStatusCode.unknown.rawValue diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 9d873f99..8bcf028d 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -61,7 +61,6 @@ public class QuicStream: @unchecked Sendable { // Deinitializer to ensure resources are cleaned up deinit { - close() streamLogger.info("QuicStream Deinit") } @@ -222,8 +221,10 @@ extension QuicStream { if let clientContext = event.pointee.SEND_COMPLETE.ClientContext { free(clientContext) } + streamLogger.info("[\(String(describing: stream))] Stream send completed") case QUIC_STREAM_EVENT_RECEIVE: + let bufferCount: UInt32 = event.pointee.RECEIVE.BufferCount let buffers = event.pointee.RECEIVE.Buffers var receivedData = Data() @@ -233,7 +234,13 @@ extension QuicStream { let bufferData = Data(bytes: buffer.Buffer, count: bufferLength) receivedData.append(bufferData) } + if receivedData.count > 0 { + if event.pointee.RECEIVE.Flags.rawValue & QUIC_RECEIVE_FLAG_FIN.rawValue != 0 { + streamLogger.warning("[\(String(describing: stream))] FIN received in QUIC stream") + quicStream.messageHandler?.didReceiveMessage(quicStream, message: QuicMessage(type: .changeStreamType, data: nil)) + quicStream.kind = .commonEphemeral + } if let continuation = quicStream.sendCompletion { continuation.resume(returning: QuicMessage(type: .received, data: receivedData)) quicStream.sendCompletion = nil @@ -244,16 +251,11 @@ extension QuicStream { } case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: - streamLogger.info("[\(String(describing: stream))] Peer send shutdown") - - quicStream.messageHandler?.didReceiveMessage( - quicStream, message: QuicMessage(type: .sendShutdown, data: nil) - ) + streamLogger.warning("[\(String(describing: stream))] Peer send shutdown") if quicStream.kind == .uniquePersistent { - quicStream.kind = .commonEphemeral - streamLogger.warning( - "[\(String(describing: stream))] Changing stream to commonEphemeral" - ) + status = + (quicStream.api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0)) + .status } case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: @@ -261,9 +263,6 @@ extension QuicStream { status = (quicStream.api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)) .status - quicStream.messageHandler?.didReceiveError( - quicStream, error: QuicError.invalidStatus(status: status.code) - ) case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: streamLogger.info("[\(String(describing: stream))] All done") diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 926f223a..e9309ca9 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -60,12 +60,12 @@ import Utils // Example subscription to PeerMessageReceived let token1 = await eventBus.subscribe(PeerMessageReceived.self) { event in print( - "Received message from peer messageID: \(event.messageID), message: \(event.message)" + "Received message from peer messageID: \(event.messageID), message: message: \(String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }))" ) let status: QuicStatus = await peer.respond( to: event.messageID, with: Message(data: event.message.data!) ) - print("Peer sent: \(status)") + print("Peer sent status: \(status.isFailed ? "Failed" : "Successed")") } // Example subscription to PeerErrorReceived @@ -75,7 +75,7 @@ import Utils ) } - _ = try await group.next().scheduleTask(in: .seconds(5)) { + _ = try await group.next().scheduleTask(in: .seconds(10)) { Task { await eventBus.unsubscribe(token: token1) await eventBus.unsubscribe(token: token2) @@ -83,7 +83,9 @@ import Utils } }.futureResult.get() - try await group.next().scheduleTask(in: .seconds(10)) {}.futureResult.get() + try await group.next().scheduleTask(in: .seconds(5)) { + print("task end") + }.futureResult.get() } catch { print("Failed to start peer: \(error)") @@ -179,7 +181,7 @@ import Utils } }.futureResult.get() - _ = try await group.next().scheduleTask(in: .seconds(100)) { + _ = try await group.next().scheduleTask(in: .seconds(5)) { Task { await eventBus1.unsubscribe(token: token1) await eventBus2.unsubscribe(token: token2) diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index 73390106..c4765778 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -18,7 +18,7 @@ import Testing ipAddress: "127.0.0.1", port: 4568 ), messageHandler: self ) - quicServer.closeSync() +// await quicServer.close() try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() } catch { From 6f91f9806489aa7e60a745ae3d8a471bf5862d54 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 6 Oct 2024 15:12:50 +0800 Subject: [PATCH 64/89] update quic client --- Networking/Sources/Networking/Peer.swift | 1 - .../Sources/Networking/msquic/QuicClient.swift | 10 ---------- .../Sources/Networking/msquic/QuicConnection.swift | 5 ----- .../Sources/Networking/msquic/QuicListener.swift | 5 ----- .../Sources/Networking/msquic/QuicServer.swift | 8 -------- .../Sources/Networking/msquic/QuicStream.swift | 14 -------------- 6 files changed, 43 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 23748840..3de36aca 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -163,7 +163,6 @@ public actor Peer { } private func removeClient(client: QuicClient) async { - peerLogger.info("remove client") let peerAddr = await client.getNetAddr() await client.close() _ = clients.removeValue(forKey: peerAddr) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 7815214a..a8a7436d 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -53,10 +53,6 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { try connection?.start(ipAddress: config.ipAddress, port: config.port) } - deinit { - clientLogger.info("QuicClient Deinit") - } - // Asynchronous send method that waits for a QuicMessage reply public func send(message: Data) async throws -> QuicMessage { try await send(message: message, streamKind: .uniquePersistent) @@ -95,27 +91,21 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } public func close() async { - clientLogger.info(" [\(getNetAddr())] client close") - guard let connection else { return } await connection.close() self.connection = nil - clientLogger.info(" [\(getNetAddr())] client connection close") guard let configuration else { return } api?.pointee.ConfigurationClose(configuration) self.configuration = nil - clientLogger.info(" [\(getNetAddr())] client configuration close") guard let registration else { return } api?.pointee.RegistrationClose(registration) self.registration = nil - clientLogger.info(" [\(getNetAddr())] client registration close") guard let api else { return } MsQuicClose(api) self.api = nil - clientLogger.info("[\(getNetAddr())] QuicClient Close") } public func didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index ba845ae0..383c1c55 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -112,11 +112,6 @@ public class QuicConnection: @unchecked Sendable { } } - // Deinitializer to ensure resources are cleaned up - deinit { - logger.info("QuicConnection Deinit") - } - // Sets the callback handler for the connection func setCallbackHandler() -> QuicStatus { guard let api, let connection, let configuration else { diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 2f5c0a7d..ef292a88 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -60,10 +60,6 @@ public class QuicListener: @unchecked Sendable { try openListener(port: config.port, listener: &listener) } - deinit { - listenLogger.trace("QuicListener Deinit") - } - private func openListener(port: UInt16, listener: inout HQuic?) throws { // Open the listener let status = @@ -122,7 +118,6 @@ public class QuicListener: @unchecked Sendable { .takeUnretainedValue() switch event.pointee.Type { case QUIC_LISTENER_EVENT_NEW_CONNECTION: - listenLogger.debug("New connection") let connection: HQuic = event.pointee.NEW_CONNECTION.Connection guard let api = listener.api else { return status diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 7dd7fac5..4e4a941e 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -53,27 +53,19 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { ) } - deinit { - serverLogger.info("QuicServer Deinit") - } - public func close() async { guard let listener else { return } await listener.close() self.listener = nil - serverLogger.debug("QuicListener close") guard let configuration else { return } api?.pointee.ConfigurationClose(configuration) self.configuration = nil - serverLogger.debug("configuration close") guard let registration else { return } api?.pointee.RegistrationClose(registration) self.registration = nil - serverLogger.debug("registration close") guard let api else { return } MsQuicClose(api) self.api = nil - serverLogger.debug("QuicServer Close") } // Respond to a message with a specific messageID using Data diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 8bcf028d..e1bb8ea4 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -59,11 +59,6 @@ public class QuicStream: @unchecked Sendable { } } - // Deinitializer to ensure resources are cleaned up - deinit { - streamLogger.info("QuicStream Deinit") - } - // Opens a stream with the specified kind private func openStream(_: StreamKind = .commonEphemeral) throws { let status = @@ -138,10 +133,6 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN - streamLogger - .info( - "[\(String(describing: stream))] flags \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")" - ) status = (api?.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw)).status if status.isFailed { streamLogger.error("StreamSend failed, \(status)!") @@ -177,11 +168,6 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN - streamLogger - .info( - "[\(String(describing: stream))] flags \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")" - ) - return try await withCheckedThrowingContinuation { [weak self] continuation in guard let self else { continuation.resume(throwing: QuicError.sendFailed) From cc2b8b414a82d213a23973d85a70f45a107b2539 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Sun, 6 Oct 2024 15:34:58 +0800 Subject: [PATCH 65/89] update quic --- .../Networking/msquic/QuicClient.swift | 28 +++++++++++-------- .../Networking/msquic/QuicConfig.swift | 2 +- .../Networking/msquic/QuicServer.swift | 28 +++++++++++-------- .../Tests/NetworkingTests/PeerTest.swift | 12 ++++---- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index a8a7436d..e905037c 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -91,21 +91,25 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } public func close() async { - guard let connection else { return } - await connection.close() - self.connection = nil + if let connection { + await connection.close() + self.connection = nil + } - guard let configuration else { return } - api?.pointee.ConfigurationClose(configuration) - self.configuration = nil + if let configuration { + api?.pointee.ConfigurationClose(configuration) + self.configuration = nil + } - guard let registration else { return } - api?.pointee.RegistrationClose(registration) - self.registration = nil + if let registration { + api?.pointee.RegistrationClose(registration) + self.registration = nil + } - guard let api else { return } - MsQuicClose(api) - self.api = nil + if let api { + MsQuicClose(api) + self.api = nil + } } public func didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 755cd647..02420724 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 20000 + settings.IdleTimeoutMs = 3000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 4e4a941e..f1d5565b 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -54,18 +54,22 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { } public func close() async { - guard let listener else { return } - await listener.close() - self.listener = nil - guard let configuration else { return } - api?.pointee.ConfigurationClose(configuration) - self.configuration = nil - guard let registration else { return } - api?.pointee.RegistrationClose(registration) - self.registration = nil - guard let api else { return } - MsQuicClose(api) - self.api = nil + if let listener { + await listener.close() + self.listener = nil + } + if let configuration { + api?.pointee.ConfigurationClose(configuration) + self.configuration = nil + } + if let registration { + api?.pointee.RegistrationClose(registration) + self.registration = nil + } + if let api { + MsQuicClose(api) + self.api = nil + } } // Respond to a message with a specific messageID using Data diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index e9309ca9..acab2204 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -59,8 +59,9 @@ import Utils // Example subscription to PeerMessageReceived let token1 = await eventBus.subscribe(PeerMessageReceived.self) { event in + let message = String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }) print( - "Received message from peer messageID: \(event.messageID), message: message: \(String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }))" + "Received message from peer messageID: \(event.messageID), message: \(message))" ) let status: QuicStatus = await peer.respond( to: event.messageID, with: Message(data: event.message.data!) @@ -117,9 +118,9 @@ import Utils // Subscribe to PeerMessageReceived for peer1 let token1 = await eventBus1.subscribe(PeerMessageReceived.self) { event in - + let message = String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }) print( - "Peer1 received message from messageID: \(event.messageID), message: \(String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }))" + "Peer1 received message from messageID: \(event.messageID), message: \(message))" ) let status: QuicStatus = await peer1.respond(to: event.messageID, with: event.message.data!) print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") @@ -127,8 +128,9 @@ import Utils // Subscribe to PeerMessageReceived for peer2 let token2 = await eventBus2.subscribe(PeerMessageReceived.self) { event in + let message = String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }) print( - "Peer2 received message from messageID: \(event.messageID), message: \(String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }))" + "Peer2 received message from messageID: \(event.messageID), message: \(message))" ) let status: QuicStatus = await peer2.respond(to: event.messageID, with: event.message.data!) print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") @@ -181,7 +183,7 @@ import Utils } }.futureResult.get() - _ = try await group.next().scheduleTask(in: .seconds(5)) { + _ = try await group.next().scheduleTask(in: .seconds(50)) { Task { await eventBus1.unsubscribe(token: token1) await eventBus2.unsubscribe(token: token2) From 67b120615acf4d9e6fbf48d8efdf9cc00c1a0c09 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 07:29:59 +0800 Subject: [PATCH 66/89] update peer --- Networking/Sources/Networking/Peer.swift | 2 +- .../Sources/Networking/msquic/QuicListener.swift | 10 +++------- Networking/Sources/Networking/msquic/QuicStream.swift | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 3de36aca..2f21755a 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -52,7 +52,7 @@ public actor Peer { deinit { closeSync() - peerLogger.info("Peer Deinit") + peerLogger.trace("Peer Deinit") } nonisolated func closeSync() { diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index ef292a88..4d655598 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -156,7 +156,9 @@ extension QuicListener: QuicConnectionMessageHandler { ) { switch message.type { case .shutdownComplete: - removeConnection(connection) + Task { + await connectionsManager.remove(connection) + } case .received: if let stream, let messageHandler { Task { @@ -182,10 +184,4 @@ extension QuicListener: QuicConnectionMessageHandler { } } } - - private func removeConnection(_ connection: QuicConnection) { - Task { - await connectionsManager.remove(connection) - } - } } diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index e1bb8ea4..98a68904 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -91,7 +91,7 @@ public class QuicStream: @unchecked Sendable { api?.pointee.StreamClose(stream) self.stream = nil } - streamLogger.info("QuicStream close") + streamLogger.debug("QuicStream close") } // Sets the callback handler for the stream From 0a8f01ed7d20543e944bb81ac42a85a9ac872044 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 08:01:22 +0800 Subject: [PATCH 67/89] update peer --- Networking/Sources/Networking/Peer.swift | 46 +-- .../Tests/NetworkingTests/PeerTest.swift | 342 +++++++++--------- .../NetworkingTests/QuicClientTest.swift | 73 ++-- .../NetworkingTests/QuicServerTest.swift | 63 ++-- 4 files changed, 257 insertions(+), 267 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 7ce92af0..761d3e31 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -2,29 +2,18 @@ import Foundation import Logging import Utils -// Logger for Peer let peerLogger = Logger(label: "PeerServer") -// Define message types public enum PeerMessageType: Sendable { - case uniquePersistent + case uniquePersistent // most messages type case commonEphemeral - case unknown } -// Define PeerMessage protocol public protocol PeerMessage: Equatable, Sendable { func getData() -> Data func getMessageType() -> PeerMessageType } -extension PeerMessage { - public func getMessageType() -> PeerMessageType { - .uniquePersistent - } -} - -// Define events public struct PeerMessageReceived: Event { public let messageID: Int64 public let message: QuicMessage @@ -39,7 +28,7 @@ public struct PeerErrorReceived: Event { // Define the Peer actor public actor Peer { private let config: QuicConfig - private var quicServer: QuicServer? + private var quicServer: QuicServer! private var clients: [NetAddr: QuicClient] private let eventBus: EventBus @@ -66,35 +55,33 @@ public actor Peer { await client.close() } clients.removeAll() - await quicServer?.close() + await quicServer.close() } // Respond to a message with a specific messageID using Data func respond(to messageID: Int64, with data: Data) async -> QuicStatus { - await quicServer?.respondGetStatus(to: messageID, with: data) - ?? QuicStatusCode.internalError.rawValue + await quicServer.respondGetStatus(to: messageID, with: data) } // Respond to a message with a specific messageID using PeerMessage func respond(to messageID: Int64, with message: any PeerMessage) async -> QuicStatus { let messageType = message.getMessageType() - return await quicServer? + return await quicServer .respondGetStatus( to: messageID, with: message.getData(), kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral ) - ?? QuicStatusCode.internalError.rawValue } // Respond to a message with a specific messageID using PeerMessage (async throws) func respond(to messageID: Int64, with message: any PeerMessage) async throws { let messageType = message.getMessageType() - let quicMessage = try await quicServer?.respondGetMessage( + let quicMessage = try await quicServer.respondGetMessage( to: messageID, with: message.getData(), kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral ) - if quicMessage?.type != .received { + if quicMessage.type != .received { throw QuicError.sendFailed } } @@ -175,23 +162,18 @@ public actor Peer { // QuicClientMessageHandler methods extension Peer: QuicClientMessageHandler { - public func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) { + public func didReceiveMessage(quicClient: QuicClient, message: QuicMessage) async { switch message.type { case .shutdownComplete: - Task { [weak self] in - guard let self else { return } - await removeClient(client: quicClient) - } + await removeClient(client: quicClient) default: break } } - public func didReceiveError(quicClient _: QuicClient, error: QuicError) { + public func didReceiveError(quicClient _: QuicClient, error: QuicError) async { peerLogger.error("Failed to receive message: \(error)") - Task { - await eventBus.publish(PeerErrorReceived(messageID: nil, error: error)) - } + await eventBus.publish(PeerErrorReceived(messageID: nil, error: error)) } } @@ -208,10 +190,8 @@ extension Peer: QuicServerMessageHandler { } } - public func didReceiveError(messageID: Int64, error: QuicError) { + public func didReceiveError(messageID: Int64, error: QuicError) async { peerLogger.error("Failed to receive message: \(error)") - Task { - await eventBus.publish(PeerErrorReceived(messageID: messageID, error: error)) - } + await eventBus.publish(PeerErrorReceived(messageID: messageID, error: error)) } } diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index acab2204..45097435 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -8,194 +8,208 @@ import Utils #if os(macOS) import CoreFoundation import Security +#endif - struct Message: PeerMessage { - public let data: Data - public let type: PeerMessageType? - public init(data: Data) { - self.data = data - type = nil - } - - public init(data: Data, type: PeerMessageType) { - self.data = data - self.type = type - } - - public func getMessageType() -> PeerMessageType { - type ?? .uniquePersistent - } +struct Message: PeerMessage { + public let data: Data + public let type: PeerMessageType + public init(data: Data) { + self.data = data + type = .uniquePersistent + } - public func getData() -> Data { - data - } + public init(data: Data, type: PeerMessageType) { + self.data = data + self.type = type } - let cert = Bundle.module.path(forResource: "server", ofType: "cert")! - let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! + public func getMessageType() -> PeerMessageType { + type + } - final class PeerTests { - @Test func startPeer() async throws { + public func getData() -> Data { + data + } +} + +let cert = Bundle.module.path(forResource: "server", ofType: "cert")! +let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! + +final class PeerTests { + @Test func startPeer() async throws { + do { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + // Example instantiation of Peer with EventBus + let eventBus = EventBus() + let peer = try await Peer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), + eventBus: eventBus + ) do { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - // Example instantiation of Peer with EventBus - let eventBus = EventBus() - let peer = try await Peer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4568 - ), - eventBus: eventBus + let message: QuicMessage = try await peer.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message(data: Data("Hello, World!".utf8)) ) - do { - let message: QuicMessage = try await peer.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4569), - with: Message(data: Data("Hello, World!".utf8)) - ) - print("Peer message got: \(message)") - } catch { - print("Failed to send: \(error)") - } + print("Peer message got: \(message)") + } catch { + print("Failed to send: \(error)") + } - // Example subscription to PeerMessageReceived - let token1 = await eventBus.subscribe(PeerMessageReceived.self) { event in - let message = String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }) - print( - "Received message from peer messageID: \(event.messageID), message: \(message))" - ) - let status: QuicStatus = await peer.respond( - to: event.messageID, with: Message(data: event.message.data!) - ) - print("Peer sent status: \(status.isFailed ? "Failed" : "Successed")") - } + // Example subscription to PeerMessageReceived + let token1 = await eventBus.subscribe(PeerMessageReceived.self) { event in + let message = String( + [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } + ) + print( + "Received message from peer messageID: \(event.messageID), message: \(message))" + ) + let status: QuicStatus = await peer.respond( + to: event.messageID, with: Message(data: event.message.data!) + ) + print("Peer sent status: \(status.isFailed ? "Failed" : "Successed")") + } - // Example subscription to PeerErrorReceived - let token2 = await eventBus.subscribe(PeerErrorReceived.self) { event in - print( - "Received error from peer messageID: \(event.messageID ?? -1), error: \(event.error)" - ) - } + // Example subscription to PeerErrorReceived + let token2 = await eventBus.subscribe(PeerErrorReceived.self) { event in + print( + "Received error from peer messageID: \(event.messageID ?? -1), error: \(event.error)" + ) + } - _ = try await group.next().scheduleTask(in: .seconds(10)) { - Task { - await eventBus.unsubscribe(token: token1) - await eventBus.unsubscribe(token: token2) - print("eventBus unsubscribe") - } - }.futureResult.get() + _ = try await group.next().scheduleTask(in: .seconds(10)) { + Task { + await eventBus.unsubscribe(token: token1) + await eventBus.unsubscribe(token: token2) + print("eventBus unsubscribe") + } + }.futureResult.get() - try await group.next().scheduleTask(in: .seconds(5)) { - print("task end") - }.futureResult.get() + try await group.next().scheduleTask(in: .seconds(5)) { + print("task end") + }.futureResult.get() - } catch { - print("Failed to start peer: \(error)") - } + } catch { + print("Failed to start peer: \(error)") } + } - @Test func testPeerCommunication() async throws { - do { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let eventBus1 = EventBus() - let eventBus2 = EventBus() - - // Create two Peer instances - let peer1 = try await Peer( - config: QuicConfig( - id: "public-key1", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4568 - ), - eventBus: eventBus1 + @Test func testPeerCommunication() async throws { + do { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let eventBus1 = EventBus() + let eventBus2 = EventBus() + + // Create two Peer instances + let peer1 = try await Peer( + config: QuicConfig( + id: "public-key1", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), + eventBus: eventBus1 + ) + + let peer2 = try await Peer( + config: QuicConfig( + id: "public-key2", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4569 + ), + eventBus: eventBus2 + ) + + // Subscribe to PeerMessageReceived for peer1 + let token1 = await eventBus1.subscribe(PeerMessageReceived.self) { event in + let message = String( + [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } ) - - let peer2 = try await Peer( - config: QuicConfig( - id: "public-key2", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4569 - ), - eventBus: eventBus2 + print( + "Peer1 received message from messageID: \(event.messageID), message: \(message))" ) + let status: QuicStatus = await peer1.respond( + to: event.messageID, with: event.message.data! + ) + print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") + } - // Subscribe to PeerMessageReceived for peer1 - let token1 = await eventBus1.subscribe(PeerMessageReceived.self) { event in - let message = String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }) - print( - "Peer1 received message from messageID: \(event.messageID), message: \(message))" - ) - let status: QuicStatus = await peer1.respond(to: event.messageID, with: event.message.data!) - print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") - } - - // Subscribe to PeerMessageReceived for peer2 - let token2 = await eventBus2.subscribe(PeerMessageReceived.self) { event in - let message = String([UInt8](event.message.data!).map { Character(UnicodeScalar($0)) }) - print( - "Peer2 received message from messageID: \(event.messageID), message: \(message))" - ) - let status: QuicStatus = await peer2.respond(to: event.messageID, with: event.message.data!) - print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") - } + // Subscribe to PeerMessageReceived for peer2 + let token2 = await eventBus2.subscribe(PeerMessageReceived.self) { event in + let message = String( + [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } + ) + print( + "Peer2 received message from messageID: \(event.messageID), message: \(message))" + ) + let status: QuicStatus = await peer2.respond( + to: event.messageID, with: event.message.data! + ) + print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") + } - // Schedule message sending after 5 seconds - _ = try await group.next().scheduleTask(in: .seconds(2)) { - Task { - do { - for i in 1 ... 5 { - let messageToPeer2: QuicMessage = try await peer1.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4569), - with: Message( - data: Data("Hello from Peer1 - Message \(i)".utf8), - type: PeerMessageType.commonEphemeral - ) + // Schedule message sending after 5 seconds + _ = try await group.next().scheduleTask(in: .seconds(2)) { + Task { + do { + for i in 1 ... 5 { + let messageToPeer2: QuicMessage = try await peer1.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message( + data: Data("Hello from Peer1 - Message \(i)".utf8), + type: PeerMessageType.commonEphemeral ) - print("Peer1 got message: \(String([UInt8](messageToPeer2.data!).map { Character(UnicodeScalar($0)) }))") - let messageToPeer1: QuicMessage = try await peer2.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4568), - with: Message( - data: Data("Hello from Peer2 - Message \(i)".utf8), - type: PeerMessageType.commonEphemeral - ) + ) + print( + "Peer1 got message: \(String([UInt8](messageToPeer2.data!).map { Character(UnicodeScalar($0)) }))" + ) + let messageToPeer1: QuicMessage = try await peer2.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4568), + with: Message( + data: Data("Hello from Peer2 - Message \(i)".utf8), + type: PeerMessageType.commonEphemeral ) - print("Peer2 got message: \(String([UInt8](messageToPeer1.data!).map { Character(UnicodeScalar($0)) }))") - } - - for i in 6 ... 10 { - let messageToPeer2: QuicMessage = try await peer1.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4569), - with: Message( - data: Data("Hello from Peer1 - Message \(i)".utf8), - type: PeerMessageType.uniquePersistent - ) + ) + print( + "Peer2 got message: \(String([UInt8](messageToPeer1.data!).map { Character(UnicodeScalar($0)) }))" + ) + } + + for i in 6 ... 10 { + let messageToPeer2: QuicMessage = try await peer1.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message( + data: Data("Hello from Peer1 - Message \(i)".utf8), + type: PeerMessageType.uniquePersistent ) - print("Peer1 sent message \(i): \(messageToPeer2)") - let messageToPeer1: QuicMessage = try await peer2.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4568), - with: Message( - data: Data("Hello from Peer2 - Message \(i)".utf8), - type: PeerMessageType.uniquePersistent - ) + ) + print("Peer1 sent message \(i): \(messageToPeer2)") + let messageToPeer1: QuicMessage = try await peer2.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4568), + with: Message( + data: Data("Hello from Peer2 - Message \(i)".utf8), + type: PeerMessageType.uniquePersistent ) - print("Peer2 sent message \(i): \(messageToPeer1)") - } - } catch { - print("Failed to send message: \(error)") + ) + print("Peer2 sent message \(i): \(messageToPeer1)") } + } catch { + print("Failed to send message: \(error)") } - }.futureResult.get() + } + }.futureResult.get() - _ = try await group.next().scheduleTask(in: .seconds(50)) { - Task { - await eventBus1.unsubscribe(token: token1) - await eventBus2.unsubscribe(token: token2) - print("eventBus unsubscribe") - } - }.futureResult.get() - try await group.next().scheduleTask(in: .seconds(5)) { - print("scheduleTask end") - }.futureResult.get() - } catch { - print("Failed about peer communication test: \(error)") - } + _ = try await group.next().scheduleTask(in: .seconds(10)) { + Task { + await eventBus1.unsubscribe(token: token1) + await eventBus2.unsubscribe(token: token2) + print("eventBus unsubscribe") + } + }.futureResult.get() + try await group.next().scheduleTask(in: .seconds(5)) { + print("scheduleTask end") + }.futureResult.get() + } catch { + print("Failed about peer communication test: \(error)") } } -#endif +} diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index 3284d636..b2fe5a90 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -7,44 +7,43 @@ import Testing #if os(macOS) import CoreFoundation import Security - - final class QuicClientTests { - @Test func start() async throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let cert = Bundle.module.path(forResource: "server", ofType: "cert")! - let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! - let quicClient = try await QuicClient( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4569 - ) +#endif +final class QuicClientTests { + @Test func start() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let cert = Bundle.module.path(forResource: "server", ofType: "cert")! + let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! + let quicClient = try await QuicClient( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4569 ) - do { - let message1 = try await quicClient.send( - message: Data("Hello, World!".utf8), streamKind: .uniquePersistent - ) - print("Client received 1: \(message1)") - let message2 = try await quicClient.send( - message: Data("Hello, swift!".utf8), streamKind: .commonEphemeral - ) - print("Client received 2: \(message2)") - let message3 = try await quicClient.send( - message: Data("Hello, how are you!".utf8), streamKind: .uniquePersistent - ) - print("Client received 3: \(message3)") - let message4 = try await quicClient.send( - message: Data("Hello, i am fine!".utf8), streamKind: .commonEphemeral - ) - print("Client received 4: \(message4)") - - } catch { - // Handle the error if sending the message fails or if the connection fails - print("Failed about quic client: \(error)") - } + ) + do { + let message1 = try await quicClient.send( + message: Data("Hello, World!".utf8), streamKind: .uniquePersistent + ) + print("Client received 1: \(message1)") + let message2 = try await quicClient.send( + message: Data("Hello, swift!".utf8), streamKind: .commonEphemeral + ) + print("Client received 2: \(message2)") + let message3 = try await quicClient.send( + message: Data("Hello, how are you!".utf8), streamKind: .uniquePersistent + ) + print("Client received 3: \(message3)") + let message4 = try await quicClient.send( + message: Data("Hello, i am fine!".utf8), streamKind: .commonEphemeral + ) + print("Client received 4: \(message4)") - try await group.next().scheduleTask(in: .seconds(5)) { - print("scheduleTask: 5s") - }.futureResult.get() + } catch { + // Handle the error if sending the message fails or if the connection fails + print("Failed about quic client: \(error)") } + + try await group.next().scheduleTask(in: .seconds(5)) { + print("scheduleTask: 5s") + }.futureResult.get() } -#endif +} diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index c4765778..c22d4392 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -7,42 +7,39 @@ import Testing #if os(macOS) import CoreFoundation import Security - - final class QuicServerTests { - @Test func start() async throws { - do { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let quicServer = try await QuicServer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4568 - ), messageHandler: self - ) -// await quicServer.close() - - try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() - } catch { - print("Failed to start quic server: \(error)") - } +#endif +final class QuicServerTests { + @Test func start() async throws { + do { + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let quicServer = try await QuicServer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), messageHandler: self + ) + try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() + } catch { + print("Failed to start quic server: \(error)") } } +} - extension QuicServerTests: QuicServerMessageHandler { - func didReceiveMessage(messageID: Int64, message: QuicMessage) async { - switch message.type { - case .received: - print("Server received message with ID \(messageID): \(message)") - case .shutdownComplete: - print("Server shutdown complete") - case .unknown: - print("Server unknown") - default: - break - } +extension QuicServerTests: QuicServerMessageHandler { + func didReceiveMessage(messageID: Int64, message: QuicMessage) async { + switch message.type { + case .received: + print("Server received message with ID \(messageID): \(message)") + case .shutdownComplete: + print("Server shutdown complete") + case .unknown: + print("Server unknown") + default: + break } + } - func didReceiveError(messageID _: Int64, error: QuicError) async { - print("Server error: \(error)") - } + func didReceiveError(messageID _: Int64, error: QuicError) async { + print("Server error: \(error)") } -#endif +} From f6168a2452f2c3c70a4d2a64d79f180470607b06 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 08:04:41 +0800 Subject: [PATCH 68/89] update quicclient --- Networking/Sources/Networking/msquic/QuicClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index e905037c..29263fe7 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -16,7 +16,7 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { private var configuration: HQuic? private var connection: QuicConnection? private let config: QuicConfig - private weak var messageHandler: Peer? + private weak var messageHandler: QuicClientMessageHandler? public init(config: QuicConfig, messageHandler: Peer? = nil) async throws { self.config = config From 4ab5628c4d51ce925b45fbfe880b2e1e0f61cf08 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 08:06:35 +0800 Subject: [PATCH 69/89] QuicClient --- Networking/Sources/Networking/msquic/QuicClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 29263fe7..16e2b41a 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -18,7 +18,7 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { private let config: QuicConfig private weak var messageHandler: QuicClientMessageHandler? - public init(config: QuicConfig, messageHandler: Peer? = nil) async throws { + public init(config: QuicConfig, messageHandler: QuicClientMessageHandler? = nil) async throws { self.config = config self.messageHandler = messageHandler var rawPointer: UnsafeRawPointer? From b03dc81597c668c15ddd01c00cf56494d5d36ba2 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 08:30:13 +0800 Subject: [PATCH 70/89] update quic --- .../Sources/Networking/msquic/QuicConnection.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 383c1c55..7c85da94 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -39,14 +39,17 @@ actor StreamManager { commonStreams.append(stream) } + func commonContains(_ stream: QuicStream) -> Bool { + commonStreams.contains { $0 === stream } + } + func removeCommonStream(_ stream: QuicStream) { commonStreams.removeAll(where: { $0 === stream }) } func changeTypeToCommon(_ stream: QuicStream) { - stream.kind = .commonEphemeral - removeUniqueStream(kind: stream.kind) removeCommonStream(stream) + stream.kind = .commonEphemeral addCommonStream(stream) } @@ -169,10 +172,11 @@ public class QuicConnection: @unchecked Sendable { // Removes a stream from the connection func removeStream(stream: QuicStream) async { stream.close() - if stream.kind == .uniquePersistent { - await streamManager.removeUniqueStream(kind: stream.kind) - } else { + let cointain = await streamManager.commonContains(stream) + if cointain { await streamManager.removeCommonStream(stream) + } else { + await streamManager.removeUniqueStream(kind: stream.kind) } } From a163b6081d0d3104af3824ae6d39a31c78c1ceda Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 09:05:17 +0800 Subject: [PATCH 71/89] update --- JAMTests/jamtestvectors | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JAMTests/jamtestvectors b/JAMTests/jamtestvectors index 4fdcf95a..03685594 160000 --- a/JAMTests/jamtestvectors +++ b/JAMTests/jamtestvectors @@ -1 +1 @@ -Subproject commit 4fdcf95aeed04d53bb4198373925d34f63069059 +Subproject commit 0368559435b2ae2431520b6aa3897dae6b42cf6b From 55d5f9ec1fd18fd3476874e4daa2fd5d4147f435 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 09:06:43 +0800 Subject: [PATCH 72/89] no message --- JAMTests/jamtestvectors | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JAMTests/jamtestvectors b/JAMTests/jamtestvectors index 03685594..4d19d0dd 160000 --- a/JAMTests/jamtestvectors +++ b/JAMTests/jamtestvectors @@ -1 +1 @@ -Subproject commit 0368559435b2ae2431520b6aa3897dae6b42cf6b +Subproject commit 4d19d0dd0a217c27976cc408495e65a1f8b6e49c From fafa8a81e34d6b9c6debd701a48ac25cbb916478 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 09:24:49 +0800 Subject: [PATCH 73/89] jam test --- JAMTests/jamtestvectors | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JAMTests/jamtestvectors b/JAMTests/jamtestvectors index 4d19d0dd..4fdcf95a 160000 --- a/JAMTests/jamtestvectors +++ b/JAMTests/jamtestvectors @@ -1 +1 @@ -Subproject commit 4d19d0dd0a217c27976cc408495e65a1f8b6e49c +Subproject commit 4fdcf95aeed04d53bb4198373925d34f63069059 From 886283f874ccabeea16a186c606536949af27a52 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 10:23:19 +0800 Subject: [PATCH 74/89] Update Networking/Sources/Networking/msquic/QuicConnection.swift Co-authored-by: Xiliang Chen --- Networking/Sources/Networking/msquic/QuicConnection.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 7c85da94..f3561e3c 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -292,7 +292,6 @@ extension QuicConnection: QuicStreamMessageHandler { Task { if stream.kind == .uniquePersistent { await streamManager.changeTypeToCommon(stream) - logger.info("changeStreamType") } } default: From 3fff041f114323ab2b3ff9f8027b342599786cd8 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 10:37:49 +0800 Subject: [PATCH 75/89] update peer --- Networking/Sources/Networking/Peer.swift | 35 ++++++++++-------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 761d3e31..b22be454 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -40,22 +40,16 @@ public actor Peer { } deinit { - closeSync() - peerLogger.trace("Peer Deinit") - } - - nonisolated func closeSync() { Task { [weak self] in - await self?.close() // Using weak self to avoid retain cycle - } - } - - private func close() async { - for client in clients.values { - await client.close() + guard let self else { return } + + var clients = await self.clients + for client in clients.values { + await client.close() + } + clients.removeAll() + await self.quicServer.close() } - clients.removeAll() - await quicServer.close() } // Respond to a message with a specific messageID using Data @@ -66,12 +60,13 @@ public actor Peer { // Respond to a message with a specific messageID using PeerMessage func respond(to messageID: Int64, with message: any PeerMessage) async -> QuicStatus { let messageType = message.getMessageType() - return await quicServer - .respondGetStatus( - to: messageID, - with: message.getData(), - kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral - ) + return + await quicServer + .respondGetStatus( + to: messageID, + with: message.getData(), + kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral + ) } // Respond to a message with a specific messageID using PeerMessage (async throws) From c6132b9fbeb7f8afb03c4476a7a4b5db159368c3 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 10:55:37 +0800 Subject: [PATCH 76/89] update peer --- .../Networking/msquic/QuicClient.swift | 12 ++---- .../Networking/msquic/QuicConnection.swift | 38 ++++++++----------- .../Networking/msquic/QuicListener.swift | 22 +++++------ .../Networking/msquic/QuicServer.swift | 13 +++---- .../Networking/msquic/QuicStream.swift | 33 ++++++++-------- 5 files changed, 49 insertions(+), 69 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 16e2b41a..49607858 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -11,7 +11,7 @@ public protocol QuicClientMessageHandler: AnyObject, Sendable { } public actor QuicClient: Sendable, QuicConnectionMessageHandler { - private var api: UnsafePointer? + private var api: UnsafePointer private var registration: HQuic? private var configuration: HQuic? private var connection: QuicConnection? @@ -97,19 +97,15 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } if let configuration { - api?.pointee.ConfigurationClose(configuration) + api.pointee.ConfigurationClose(configuration) self.configuration = nil } if let registration { - api?.pointee.RegistrationClose(registration) + api.pointee.RegistrationClose(registration) self.registration = nil } - - if let api { - MsQuicClose(api) - self.api = nil - } + MsQuicClose(api) } public func didReceiveMessage( diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index f3561e3c..f0117034 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -70,7 +70,7 @@ actor StreamManager { public class QuicConnection: @unchecked Sendable { private var connection: HQuic? - private var api: UnsafePointer? + private let api: UnsafePointer private var registration: HQuic? private var configuration: HQuic? private var streamManager: StreamManager @@ -79,7 +79,7 @@ public class QuicConnection: @unchecked Sendable { // Initializer for creating a new connection init( - api: UnsafePointer?, + api: UnsafePointer, registration: HQuic?, configuration: HQuic?, messageHandler: QuicConnectionMessageHandler? = nil @@ -99,7 +99,7 @@ public class QuicConnection: @unchecked Sendable { // Initializer for wrapping an existing connection init( - api: UnsafePointer?, registration: HQuic?, configuration: HQuic?, + api: UnsafePointer, registration: HQuic?, configuration: HQuic?, connection: HQuic?, messageHandler: QuicConnectionMessageHandler? = nil ) { self.api = api @@ -117,10 +117,6 @@ public class QuicConnection: @unchecked Sendable { // Sets the callback handler for the connection func setCallbackHandler() -> QuicStatus { - guard let api, let connection, let configuration else { - return QuicStatusCode.invalidParameter.rawValue - } - let callbackPointer = unsafeBitCast( connectionCallback, to: UnsafeMutableRawPointer?.self ) @@ -136,15 +132,14 @@ public class QuicConnection: @unchecked Sendable { // Opens the connection private func open() throws { - let status = - (api?.pointee.ConnectionOpen( - registration, - { connection, context, event -> QuicStatus in - return QuicConnection.connectionCallback( - connection: connection, context: context, event: event - ) - }, Unmanaged.passUnretained(self).toOpaque(), &connection - )).status + let status = api.pointee.ConnectionOpen( + registration, + { connection, context, event -> QuicStatus in + return QuicConnection.connectionCallback( + connection: connection, context: context, event: event + ) + }, Unmanaged.passUnretained(self).toOpaque(), &connection + ) if status.isFailed { throw QuicError.invalidStatus(status: status.code) } @@ -187,7 +182,7 @@ public class QuicConnection: @unchecked Sendable { await streamManager.closeAllCommonStreams() await streamManager.closeAllUniqueStreams() if let connection { - api?.pointee.ConnectionClose(connection) + api.pointee.ConnectionClose(connection) self.connection = nil } logger.debug("QuicConnection Close") @@ -195,11 +190,10 @@ public class QuicConnection: @unchecked Sendable { // Starts the connection with the specified IP address and port func start(ipAddress: String, port: UInt16) throws { - let status = - (api?.pointee.ConnectionStart( - connection, configuration, QUIC_ADDRESS_FAMILY(QUIC_ADDRESS_FAMILY_UNSPEC), - ipAddress, port - )).status + let status = api.pointee.ConnectionStart( + connection, configuration, QUIC_ADDRESS_FAMILY(QUIC_ADDRESS_FAMILY_UNSPEC), + ipAddress, port + ) if status.isFailed { throw QuicError.invalidStatus(status: status.code) } diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index 4d655598..b838ae9a 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -36,16 +36,16 @@ public protocol QuicListenerMessageHandler: AnyObject { } public class QuicListener: @unchecked Sendable { - private var api: UnsafePointer? - private var registration: HQuic? - private var configuration: HQuic? + private let api: UnsafePointer + private let registration: HQuic? + private let configuration: HQuic? private var config: QuicConfig private var listener: HQuic? public weak var messageHandler: QuicListenerMessageHandler? private let connectionsManager: ConnectionsManager public init( - api: UnsafePointer?, + api: UnsafePointer, registration: HQuic?, configuration: HQuic?, config: QuicConfig, @@ -63,7 +63,7 @@ public class QuicListener: @unchecked Sendable { private func openListener(port: UInt16, listener: inout HQuic?) throws { // Open the listener let status = - (api?.pointee.ListenerOpen( + api.pointee.ListenerOpen( registration, { listener, context, event -> QuicStatus in QuicListener.serverListenerCallback( @@ -71,7 +71,7 @@ public class QuicListener: @unchecked Sendable { ) }, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()), &listener - )).status + ) guard status.isSucceeded else { throw QuicError.invalidStatus(status: status.code) @@ -94,7 +94,7 @@ public class QuicListener: @unchecked Sendable { // Start the listener let startStatus: QuicStatus = - (api?.pointee.ListenerStart(listener, &alpnBuffer, 1, &address)).status + api.pointee.ListenerStart(listener, &alpnBuffer, 1, &address) guard startStatus.isSucceeded else { throw QuicError.invalidStatus(status: startStatus.code) @@ -119,12 +119,8 @@ public class QuicListener: @unchecked Sendable { switch event.pointee.Type { case QUIC_LISTENER_EVENT_NEW_CONNECTION: let connection: HQuic = event.pointee.NEW_CONNECTION.Connection - guard let api = listener.api else { - return status - } - let quicConnection = QuicConnection( - api: api, + api: listener.api, registration: listener.registration, configuration: listener.configuration, connection: connection, @@ -143,7 +139,7 @@ public class QuicListener: @unchecked Sendable { public func close() async { if let listener { - api?.pointee.ListenerClose(listener) + api.pointee.ListenerClose(listener) self.listener = nil } await connectionsManager.removeAll() diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index f1d5565b..4b3e2c07 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -11,7 +11,7 @@ public protocol QuicServerMessageHandler: AnyObject, Sendable { } public actor QuicServer: Sendable, QuicListenerMessageHandler { - private var api: UnsafePointer? + private var api: UnsafePointer private var registration: HQuic? private var configuration: HQuic? private var listener: QuicListener? @@ -48,7 +48,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { api: api, registration: registration, configuration: &configuration ) listener = try QuicListener( - api: api, registration: registration, configuration: configuration, config: config, + api: boundPointer, registration: registration, configuration: configuration, config: config, messageHandler: self ) } @@ -59,17 +59,14 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { self.listener = nil } if let configuration { - api?.pointee.ConfigurationClose(configuration) + api.pointee.ConfigurationClose(configuration) self.configuration = nil } if let registration { - api?.pointee.RegistrationClose(registration) + api.pointee.RegistrationClose(registration) self.registration = nil } - if let api { - MsQuicClose(api) - self.api = nil - } + MsQuicClose(api) } // Respond to a message with a specific messageID using Data diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 98a68904..2762d591 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -7,7 +7,6 @@ let streamLogger = Logger(label: "QuicStream") public enum StreamKind: Sendable { case uniquePersistent case commonEphemeral - case unknown } public protocol QuicStreamMessageHandler: AnyObject { @@ -17,7 +16,7 @@ public protocol QuicStreamMessageHandler: AnyObject { public class QuicStream: @unchecked Sendable { private var stream: HQuic? - private let api: UnsafePointer? + private let api: UnsafePointer private let connection: HQuic? public var kind: StreamKind private weak var messageHandler: QuicStreamMessageHandler? @@ -25,7 +24,7 @@ public class QuicStream: @unchecked Sendable { private var sendCompletion: CheckedContinuation? // Initializer for creating a new stream init( - api: UnsafePointer?, connection: HQuic?, + api: UnsafePointer, connection: HQuic?, _ streamKind: StreamKind = .uniquePersistent, messageHandler: QuicStreamMessageHandler? = nil ) throws { @@ -43,7 +42,7 @@ public class QuicStream: @unchecked Sendable { // Initializer for wrapping an existing stream init( - api: UnsafePointer?, connection: HQuic?, stream: HQuic?, + api: UnsafePointer, connection: HQuic?, stream: HQuic?, _ streamKind: StreamKind = .uniquePersistent, messageHandler: QuicStreamMessageHandler? = nil ) { @@ -62,12 +61,12 @@ public class QuicStream: @unchecked Sendable { // Opens a stream with the specified kind private func openStream(_: StreamKind = .commonEphemeral) throws { let status = - (api?.pointee.StreamOpen( + api.pointee.StreamOpen( connection, QUIC_STREAM_OPEN_FLAG_NONE, { stream, context, event -> QuicStatus in QuicStream.streamCallback(stream: stream, context: context, event: event) }, Unmanaged.passUnretained(self).toOpaque(), &stream - )).status + ) if status.isFailed { throw QuicError.invalidStatus(status: status.code) } @@ -76,7 +75,7 @@ public class QuicStream: @unchecked Sendable { // Starts the stream private func start() throws { try openStream(kind) - let status = (api?.pointee.StreamStart(stream, QUIC_STREAM_START_FLAG_NONE)).status + let status = api.pointee.StreamStart(stream, QUIC_STREAM_START_FLAG_NONE) if status.isFailed { throw QuicError.invalidStatus(status: status.code) } @@ -88,7 +87,7 @@ public class QuicStream: @unchecked Sendable { streamCallback = nil messageHandler = nil if let stream { - api?.pointee.StreamClose(stream) + api.pointee.StreamClose(stream) self.stream = nil } streamLogger.debug("QuicStream close") @@ -96,7 +95,7 @@ public class QuicStream: @unchecked Sendable { // Sets the callback handler for the stream func setCallbackHandler() { - guard let api, let stream else { + guard let stream else { return } @@ -133,11 +132,11 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN - status = (api?.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw)).status + status = api.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw) if status.isFailed { streamLogger.error("StreamSend failed, \(status)!") let shutdown: QuicStatus = - (api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)).status + api.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0) if shutdown.isFailed { streamLogger.error("StreamShutdown failed, 0x\(String(format: "%x", shutdown))!") } @@ -174,11 +173,11 @@ public class QuicStream: @unchecked Sendable { return } sendCompletion = continuation - status = (api?.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw)).status + status = api.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw) if status.isFailed { streamLogger.error("StreamSend failed, \(status)!") let shutdown: QuicStatus = - (api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)).status + api.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0) if shutdown.isFailed { streamLogger.error( "StreamShutdown failed, 0x\(String(format: "%x", shutdown))!" @@ -240,20 +239,18 @@ extension QuicStream { streamLogger.warning("[\(String(describing: stream))] Peer send shutdown") if quicStream.kind == .uniquePersistent { status = - (quicStream.api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0)) - .status + quicStream.api.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0) } case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: streamLogger.error("[\(String(describing: stream))] Peer send aborted") status = - (quicStream.api?.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0)) - .status + quicStream.api.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0) case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: streamLogger.info("[\(String(describing: stream))] All done") if event.pointee.SHUTDOWN_COMPLETE.AppCloseInProgress == 0 { - quicStream.api?.pointee.StreamClose(stream) + quicStream.api.pointee.StreamClose(stream) } if let continuation = quicStream.sendCompletion { continuation.resume(throwing: QuicError.sendFailed) From 98ddf02a96c611fedf434ea605418d7aac1fcd9c Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 11:16:06 +0800 Subject: [PATCH 77/89] update quic --- .../Networking/msquic/QuicConnection.swift | 27 +++++++++---------- .../Networking/msquic/QuicStream.swift | 6 ++++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index f0117034..358f1cc9 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -47,9 +47,18 @@ actor StreamManager { commonStreams.removeAll(where: { $0 === stream }) } + func removeStream(_ stream: QuicStream) { + let cointain = commonContains(stream) + if cointain { + removeCommonStream(stream) + } else { + removeUniqueStream(kind: stream.kind) + } + } + func changeTypeToCommon(_ stream: QuicStream) { - removeCommonStream(stream) - stream.kind = .commonEphemeral + removeStream(stream) + stream.changeTypeToCommon() addCommonStream(stream) } @@ -164,17 +173,6 @@ public class QuicConnection: @unchecked Sendable { return stream } - // Removes a stream from the connection - func removeStream(stream: QuicStream) async { - stream.close() - let cointain = await streamManager.commonContains(stream) - if cointain { - await streamManager.removeCommonStream(stream) - } else { - await streamManager.removeUniqueStream(kind: stream.kind) - } - } - // Closes the connection and cleans up resources func close() async { connectionCallback = nil @@ -280,7 +278,8 @@ extension QuicConnection: QuicStreamMessageHandler { switch message.type { case .shutdownComplete: Task { - await removeStream(stream: stream) + stream.close() + await streamManager.removeStream(stream) } case .changeStreamType: Task { diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 2762d591..eaf24e53 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -18,7 +18,7 @@ public class QuicStream: @unchecked Sendable { private var stream: HQuic? private let api: UnsafePointer private let connection: HQuic? - public var kind: StreamKind + public private(set) var kind: StreamKind private weak var messageHandler: QuicStreamMessageHandler? private var streamCallback: StreamCallback? private var sendCompletion: CheckedContinuation? @@ -93,6 +93,10 @@ public class QuicStream: @unchecked Sendable { streamLogger.debug("QuicStream close") } + func changeTypeToCommon() { + kind = .commonEphemeral + } + // Sets the callback handler for the stream func setCallbackHandler() { guard let stream else { From bff385d4b2a3c2b08e00c3171c961cbf70740c17 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 11:26:01 +0800 Subject: [PATCH 78/89] udpate quic server --- Networking/Sources/Networking/msquic/QuicConnection.swift | 4 ++-- Networking/Sources/Networking/msquic/QuicServer.swift | 4 ++-- Networking/Sources/Networking/msquic/QuicStream.swift | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 358f1cc9..d49dc5ee 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -52,7 +52,7 @@ actor StreamManager { if cointain { removeCommonStream(stream) } else { - removeUniqueStream(kind: stream.kind) + removeUniqueStream(kind: stream.getKind()) } } @@ -283,7 +283,7 @@ extension QuicConnection: QuicStreamMessageHandler { } case .changeStreamType: Task { - if stream.kind == .uniquePersistent { + if stream.getKind() == .uniquePersistent { await streamManager.changeTypeToCommon(stream) } } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 4b3e2c07..dacb702e 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -75,7 +75,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { { var status = QuicStatusCode.internalError.rawValue if let (_, stream) = pendingMessages[messageID] { - let streamKind = kind ?? stream.kind + let streamKind = kind ?? stream.getKind() pendingMessages.removeValue(forKey: messageID) status = stream.respond(with: data, kind: streamKind) @@ -94,7 +94,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { throw QuicError.messageNotFound } - let streamKind = kind ?? stream.kind + let streamKind = kind ?? stream.getKind() pendingMessages.removeValue(forKey: messageID) return try await send(stream: stream, with: data, kind: streamKind) } diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index eaf24e53..6bf1d169 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -18,7 +18,7 @@ public class QuicStream: @unchecked Sendable { private var stream: HQuic? private let api: UnsafePointer private let connection: HQuic? - public private(set) var kind: StreamKind + private var kind: StreamKind private weak var messageHandler: QuicStreamMessageHandler? private var streamCallback: StreamCallback? private var sendCompletion: CheckedContinuation? @@ -93,6 +93,10 @@ public class QuicStream: @unchecked Sendable { streamLogger.debug("QuicStream close") } + func getKind() -> StreamKind { + kind + } + func changeTypeToCommon() { kind = .commonEphemeral } From 77a94dd932261b56f1c37b63d4034c85d5092f4d Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 11:35:51 +0800 Subject: [PATCH 79/89] update peer --- .../Tests/NetworkingTests/PeerTest.swift | 62 +------------------ .../NetworkingTests/QuicClientTest.swift | 2 +- .../NetworkingTests/QuicServerTest.swift | 2 +- 3 files changed, 3 insertions(+), 63 deletions(-) diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 45097435..4a7e3a3d 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -35,67 +35,7 @@ struct Message: PeerMessage { let cert = Bundle.module.path(forResource: "server", ofType: "cert")! let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! -final class PeerTests { - @Test func startPeer() async throws { - do { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - // Example instantiation of Peer with EventBus - let eventBus = EventBus() - let peer = try await Peer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4568 - ), - eventBus: eventBus - ) - do { - let message: QuicMessage = try await peer.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4569), - with: Message(data: Data("Hello, World!".utf8)) - ) - print("Peer message got: \(message)") - } catch { - print("Failed to send: \(error)") - } - - // Example subscription to PeerMessageReceived - let token1 = await eventBus.subscribe(PeerMessageReceived.self) { event in - let message = String( - [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } - ) - print( - "Received message from peer messageID: \(event.messageID), message: \(message))" - ) - let status: QuicStatus = await peer.respond( - to: event.messageID, with: Message(data: event.message.data!) - ) - print("Peer sent status: \(status.isFailed ? "Failed" : "Successed")") - } - - // Example subscription to PeerErrorReceived - let token2 = await eventBus.subscribe(PeerErrorReceived.self) { event in - print( - "Received error from peer messageID: \(event.messageID ?? -1), error: \(event.error)" - ) - } - - _ = try await group.next().scheduleTask(in: .seconds(10)) { - Task { - await eventBus.unsubscribe(token: token1) - await eventBus.unsubscribe(token: token2) - print("eventBus unsubscribe") - } - }.futureResult.get() - - try await group.next().scheduleTask(in: .seconds(5)) { - print("task end") - }.futureResult.get() - - } catch { - print("Failed to start peer: \(error)") - } - } - +final actor PeerTests { @Test func testPeerCommunication() async throws { do { let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index b2fe5a90..fdeb4af3 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -16,7 +16,7 @@ final class QuicClientTests { let quicClient = try await QuicClient( config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4569 + ipAddress: "127.0.0.1", port: 4563 ) ) do { diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index c22d4392..7788e684 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -15,7 +15,7 @@ final class QuicServerTests { let quicServer = try await QuicServer( config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4568 + ipAddress: "127.0.0.1", port: 4561 ), messageHandler: self ) try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() From ffad634c217da1ba5c54bf3be603bc58e4e9b724 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 14:53:21 +0800 Subject: [PATCH 80/89] Update Networking/Tests/NetworkingTests/QuicServerTest.swift Co-authored-by: Xiliang Chen --- Networking/Tests/NetworkingTests/QuicServerTest.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index 7788e684..3d4d1c3e 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -10,7 +10,6 @@ import Testing #endif final class QuicServerTests { @Test func start() async throws { - do { let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) let quicServer = try await QuicServer( config: QuicConfig( @@ -19,9 +18,6 @@ final class QuicServerTests { ), messageHandler: self ) try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() - } catch { - print("Failed to start quic server: \(error)") - } } } From 36b87cc539a870f728af0cc322c85fbcbced1561 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 14:58:52 +0800 Subject: [PATCH 81/89] update test --- .../Tests/NetworkingTests/PeerTest.swift | 198 +++++++++--------- .../NetworkingTests/QuicServerTest.swift | 20 +- 2 files changed, 105 insertions(+), 113 deletions(-) diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index 4a7e3a3d..ea0bcf49 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -37,119 +37,115 @@ let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! final actor PeerTests { @Test func testPeerCommunication() async throws { - do { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let eventBus1 = EventBus() - let eventBus2 = EventBus() + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let eventBus1 = EventBus() + let eventBus2 = EventBus() - // Create two Peer instances - let peer1 = try await Peer( - config: QuicConfig( - id: "public-key1", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4568 - ), - eventBus: eventBus1 - ) + // Create two Peer instances + let peer1 = try await Peer( + config: QuicConfig( + id: "public-key1", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4568 + ), + eventBus: eventBus1 + ) - let peer2 = try await Peer( - config: QuicConfig( - id: "public-key2", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4569 - ), - eventBus: eventBus2 - ) + let peer2 = try await Peer( + config: QuicConfig( + id: "public-key2", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4569 + ), + eventBus: eventBus2 + ) - // Subscribe to PeerMessageReceived for peer1 - let token1 = await eventBus1.subscribe(PeerMessageReceived.self) { event in - let message = String( - [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } - ) - print( - "Peer1 received message from messageID: \(event.messageID), message: \(message))" - ) - let status: QuicStatus = await peer1.respond( - to: event.messageID, with: event.message.data! - ) - print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") - } + // Subscribe to PeerMessageReceived for peer1 + let token1 = await eventBus1.subscribe(PeerMessageReceived.self) { event in + let message = String( + [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } + ) + print( + "Peer1 received message from messageID: \(event.messageID), message: \(message))" + ) + let status: QuicStatus = await peer1.respond( + to: event.messageID, with: event.message.data! + ) + print("Peer1 sent response: \(status.isFailed ? "Failed" : "Success")") + } - // Subscribe to PeerMessageReceived for peer2 - let token2 = await eventBus2.subscribe(PeerMessageReceived.self) { event in - let message = String( - [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } - ) - print( - "Peer2 received message from messageID: \(event.messageID), message: \(message))" - ) - let status: QuicStatus = await peer2.respond( - to: event.messageID, with: event.message.data! - ) - print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") - } + // Subscribe to PeerMessageReceived for peer2 + let token2 = await eventBus2.subscribe(PeerMessageReceived.self) { event in + let message = String( + [UInt8](event.message.data!).map { Character(UnicodeScalar($0)) } + ) + print( + "Peer2 received message from messageID: \(event.messageID), message: \(message))" + ) + let status: QuicStatus = await peer2.respond( + to: event.messageID, with: event.message.data! + ) + print("Peer2 sent response: \(status.isFailed ? "Failed" : "Success")") + } - // Schedule message sending after 5 seconds - _ = try await group.next().scheduleTask(in: .seconds(2)) { - Task { - do { - for i in 1 ... 5 { - let messageToPeer2: QuicMessage = try await peer1.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4569), - with: Message( - data: Data("Hello from Peer1 - Message \(i)".utf8), - type: PeerMessageType.commonEphemeral - ) - ) - print( - "Peer1 got message: \(String([UInt8](messageToPeer2.data!).map { Character(UnicodeScalar($0)) }))" + // Schedule message sending after 5 seconds + _ = try await group.next().scheduleTask(in: .seconds(2)) { + Task { + do { + for i in 1 ... 5 { + let messageToPeer2: QuicMessage = try await peer1.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message( + data: Data("Hello from Peer1 - Message \(i)".utf8), + type: PeerMessageType.commonEphemeral ) - let messageToPeer1: QuicMessage = try await peer2.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4568), - with: Message( - data: Data("Hello from Peer2 - Message \(i)".utf8), - type: PeerMessageType.commonEphemeral - ) + ) + print( + "Peer1 got message: \(String([UInt8](messageToPeer2.data!).map { Character(UnicodeScalar($0)) }))" + ) + let messageToPeer1: QuicMessage = try await peer2.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4568), + with: Message( + data: Data("Hello from Peer2 - Message \(i)".utf8), + type: PeerMessageType.commonEphemeral ) - print( - "Peer2 got message: \(String([UInt8](messageToPeer1.data!).map { Character(UnicodeScalar($0)) }))" - ) - } + ) + print( + "Peer2 got message: \(String([UInt8](messageToPeer1.data!).map { Character(UnicodeScalar($0)) }))" + ) + } - for i in 6 ... 10 { - let messageToPeer2: QuicMessage = try await peer1.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4569), - with: Message( - data: Data("Hello from Peer1 - Message \(i)".utf8), - type: PeerMessageType.uniquePersistent - ) + for i in 6 ... 10 { + let messageToPeer2: QuicMessage = try await peer1.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4569), + with: Message( + data: Data("Hello from Peer1 - Message \(i)".utf8), + type: PeerMessageType.uniquePersistent ) - print("Peer1 sent message \(i): \(messageToPeer2)") - let messageToPeer1: QuicMessage = try await peer2.sendMessage( - to: NetAddr(ipAddress: "127.0.0.1", port: 4568), - with: Message( - data: Data("Hello from Peer2 - Message \(i)".utf8), - type: PeerMessageType.uniquePersistent - ) + ) + print("Peer1 sent message \(i): \(messageToPeer2)") + let messageToPeer1: QuicMessage = try await peer2.sendMessage( + to: NetAddr(ipAddress: "127.0.0.1", port: 4568), + with: Message( + data: Data("Hello from Peer2 - Message \(i)".utf8), + type: PeerMessageType.uniquePersistent ) - print("Peer2 sent message \(i): \(messageToPeer1)") - } - } catch { - print("Failed to send message: \(error)") + ) + print("Peer2 sent message \(i): \(messageToPeer1)") } + } catch { + print("Failed to send message: \(error)") } - }.futureResult.get() + } + }.futureResult.get() - _ = try await group.next().scheduleTask(in: .seconds(10)) { - Task { - await eventBus1.unsubscribe(token: token1) - await eventBus2.unsubscribe(token: token2) - print("eventBus unsubscribe") - } - }.futureResult.get() - try await group.next().scheduleTask(in: .seconds(5)) { - print("scheduleTask end") - }.futureResult.get() - } catch { - print("Failed about peer communication test: \(error)") - } + _ = try await group.next().scheduleTask(in: .seconds(10)) { + Task { + await eventBus1.unsubscribe(token: token1) + await eventBus2.unsubscribe(token: token2) + print("eventBus unsubscribe") + } + }.futureResult.get() + try await group.next().scheduleTask(in: .seconds(5)) { + print("scheduleTask end") + }.futureResult.get() } } diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index 7788e684..8cc06dc1 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -10,18 +10,14 @@ import Testing #endif final class QuicServerTests { @Test func start() async throws { - do { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let quicServer = try await QuicServer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4561 - ), messageHandler: self - ) - try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() - } catch { - print("Failed to start quic server: \(error)") - } + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let quicServer = try await QuicServer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4561 + ), messageHandler: self + ) + try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() } } From 897256de3e2b27c9839fd0e1f72491452b6d2e05 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 15:05:00 +0800 Subject: [PATCH 82/89] update peer --- Networking/Sources/Networking/msquic/QuicConnection.swift | 4 ++-- Networking/Sources/Networking/msquic/QuicServer.swift | 4 ++-- Networking/Sources/Networking/msquic/QuicStream.swift | 6 +----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index d49dc5ee..358f1cc9 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -52,7 +52,7 @@ actor StreamManager { if cointain { removeCommonStream(stream) } else { - removeUniqueStream(kind: stream.getKind()) + removeUniqueStream(kind: stream.kind) } } @@ -283,7 +283,7 @@ extension QuicConnection: QuicStreamMessageHandler { } case .changeStreamType: Task { - if stream.getKind() == .uniquePersistent { + if stream.kind == .uniquePersistent { await streamManager.changeTypeToCommon(stream) } } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index dacb702e..4b3e2c07 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -75,7 +75,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { { var status = QuicStatusCode.internalError.rawValue if let (_, stream) = pendingMessages[messageID] { - let streamKind = kind ?? stream.getKind() + let streamKind = kind ?? stream.kind pendingMessages.removeValue(forKey: messageID) status = stream.respond(with: data, kind: streamKind) @@ -94,7 +94,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { throw QuicError.messageNotFound } - let streamKind = kind ?? stream.getKind() + let streamKind = kind ?? stream.kind pendingMessages.removeValue(forKey: messageID) return try await send(stream: stream, with: data, kind: streamKind) } diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 6bf1d169..eaf24e53 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -18,7 +18,7 @@ public class QuicStream: @unchecked Sendable { private var stream: HQuic? private let api: UnsafePointer private let connection: HQuic? - private var kind: StreamKind + public private(set) var kind: StreamKind private weak var messageHandler: QuicStreamMessageHandler? private var streamCallback: StreamCallback? private var sendCompletion: CheckedContinuation? @@ -93,10 +93,6 @@ public class QuicStream: @unchecked Sendable { streamLogger.debug("QuicStream close") } - func getKind() -> StreamKind { - kind - } - func changeTypeToCommon() { kind = .commonEphemeral } From e0dfc4452eb1037b326528c1d194d1a6d158dc41 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 18:31:12 +0800 Subject: [PATCH 83/89] update peer --- Networking/Sources/Networking/Peer.swift | 88 +++++++++---------- .../Networking/msquic/QuicClient.swift | 19 ---- .../Networking/msquic/QuicConfig.swift | 2 +- .../Networking/msquic/QuicConnection.swift | 16 ++-- .../Networking/msquic/QuicServer.swift | 27 +----- .../Networking/msquic/QuicStream.swift | 72 +++------------ .../Tests/NetworkingTests/PeerTest.swift | 21 ++--- .../NetworkingTests/QuicClientTest.swift | 62 ++++++++----- .../NetworkingTests/QuicServerTest.swift | 10 ++- 9 files changed, 124 insertions(+), 193 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index b22be454..5ca2783c 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -69,24 +69,24 @@ public actor Peer { ) } - // Respond to a message with a specific messageID using PeerMessage (async throws) - func respond(to messageID: Int64, with message: any PeerMessage) async throws { - let messageType = message.getMessageType() - let quicMessage = try await quicServer.respondGetMessage( - to: messageID, with: message.getData(), - kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral - ) - if quicMessage.type != .received { - throw QuicError.sendFailed - } - } - - // Sends a message to another peer asynchronously - func sendMessage(to peer: NetAddr, with message: any PeerMessage) async throws -> QuicMessage { - let buffer = message.getData() - let messageType = message.getMessageType() - return try await sendDataToPeer(buffer, to: peer, messageType: messageType) - } +// // Respond to a message with a specific messageID using PeerMessage (async throws) +// func respond(to messageID: Int64, with message: any PeerMessage) async throws { +// let messageType = message.getMessageType() +// let quicMessage = try await quicServer.respondGetMessage( +// to: messageID, with: message.getData(), +// kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral +// ) +// if quicMessage.type != .received { +// throw QuicError.sendFailed +// } +// } + +// // Sends a message to another peer asynchronously +// func sendMessage(to peer: NetAddr, with message: any PeerMessage) async throws -> QuicMessage { +// let buffer = message.getData() +// let messageType = message.getMessageType() +// return try await sendDataToPeer(buffer, to: peer, messageType: messageType) +// } // Sends a message to another peer and returns the status func sendMessage(to peer: NetAddr, with message: any PeerMessage) async throws -> QuicStatus { @@ -95,30 +95,30 @@ public actor Peer { return try await sendDataToPeer(buffer, to: peer, messageType: messageType) } - // send message to other peer wait for response quicMessage - private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) - async throws -> QuicMessage - { - if let client = clients[peerAddr] { - // Client already exists, use it to send the data - return try await client.send( - message: data, - streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral - ) - } else { - let config = QuicConfig( - id: config.id, cert: config.cert, key: config.key, alpn: config.alpn, - ipAddress: peerAddr.ipAddress, port: peerAddr.port - ) - // Client does not exist, create a new one - let client = try await QuicClient(config: config, messageHandler: self) - clients[peerAddr] = client - return try await client.send( - message: data, - streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral - ) - } - } +// // send message to other peer wait for response quicMessage +// private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) +// async throws -> QuicMessage +// { +// if let client = clients[peerAddr] { +// // Client already exists, use it to send the data +// return try await client.send( +// message: data, +// streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral +// ) +// } else { +// let config = QuicConfig( +// id: config.id, cert: config.cert, key: config.key, alpn: config.alpn, +// ipAddress: peerAddr.ipAddress, port: peerAddr.port +// ) +// // Client does not exist, create a new one +// let client = try await QuicClient(config: config, messageHandler: self) +// clients[peerAddr] = client +// return try await client.send( +// message: data, +// streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral +// ) +// } +// } private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) async throws -> QuicStatus @@ -174,7 +174,7 @@ extension Peer: QuicClientMessageHandler { // QuicServerMessageHandler methods extension Peer: QuicServerMessageHandler { - public func didReceiveMessage(messageID: Int64, message: QuicMessage) async { + public func didReceiveMessage(server _: QuicServer, messageID: Int64, message: QuicMessage) async { switch message.type { case .received: await eventBus.publish(PeerMessageReceived(messageID: messageID, message: message)) @@ -185,7 +185,7 @@ extension Peer: QuicServerMessageHandler { } } - public func didReceiveError(messageID: Int64, error: QuicError) async { + public func didReceiveError(server _: QuicServer, messageID: Int64, error: QuicError) async { peerLogger.error("Failed to receive message: \(error)") await eventBus.publish(PeerErrorReceived(messageID: messageID, error: error)) } diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 49607858..7cce2749 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -53,25 +53,6 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { try connection?.start(ipAddress: config.ipAddress, port: config.port) } - // Asynchronous send method that waits for a QuicMessage reply - public func send(message: Data) async throws -> QuicMessage { - try await send(message: message, streamKind: .uniquePersistent) - } - - // Send method that returns a QuicStatus - public func send(message: Data, streamKind: StreamKind) async throws -> QuicMessage { - guard let connection else { - throw QuicError.getConnectionFailed - } - let sendStream: QuicStream = - if streamKind == .uniquePersistent { - try await connection.createOrGetUniquePersistentStream(kind: streamKind) - } else { - try await connection.createCommonEphemeralStream() - } - return try await sendStream.send(data: message, kind: streamKind) - } - // Send method that returns a QuicStatus public func send(data: Data, streamKind: StreamKind) async throws -> QuicStatus { guard let connection else { diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 10ea4d7c..c7c3e6e9 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 30000 + settings.IdleTimeoutMs = 10000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 358f1cc9..7e3165a7 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -236,15 +236,13 @@ extension QuicConnection { case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: logger.info("[\(String(describing: connection))] Shutdown all done") if event.pointee.SHUTDOWN_COMPLETE.AppCloseInProgress == 0 { - if let messageHandler = quicConnection.messageHandler { - Task { - await messageHandler.didReceiveMessage( - connection: quicConnection, - stream: nil, - message: QuicMessage(type: .shutdownComplete, data: nil) - ) - quicConnection.messageHandler = nil - } + Task { + await quicConnection.messageHandler?.didReceiveMessage( + connection: quicConnection, + stream: nil, + message: QuicMessage(type: .shutdownComplete, data: nil) + ) + quicConnection.messageHandler = nil } } diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 4b3e2c07..89b45d1c 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -6,8 +6,8 @@ import Utils let serverLogger: Logger = .init(label: "QuicServer") public protocol QuicServerMessageHandler: AnyObject, Sendable { - func didReceiveMessage(messageID: Int64, message: QuicMessage) async - func didReceiveError(messageID: Int64, error: QuicError) async + func didReceiveMessage(server: QuicServer, messageID: Int64, message: QuicMessage) async + func didReceiveError(server: QuicServer, messageID: Int64, error: QuicError) async } public actor QuicServer: Sendable, QuicListenerMessageHandler { @@ -77,7 +77,6 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind pendingMessages.removeValue(forKey: messageID) - status = stream.respond(with: data, kind: streamKind) } else { serverLogger.error("Message not found") @@ -85,26 +84,6 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { return status } - // Respond to a message with a specific messageID using Data - func respondGetMessage(to messageID: Int64, with data: Data, kind: StreamKind? = nil) - async throws - -> QuicMessage - { - guard let (_, stream) = pendingMessages[messageID] else { - throw QuicError.messageNotFound - } - - let streamKind = kind ?? stream.kind - pendingMessages.removeValue(forKey: messageID) - return try await send(stream: stream, with: data, kind: streamKind) - } - - private func send(stream: QuicStream, with data: Data, kind: StreamKind) - async throws -> QuicMessage - { - try await stream.send(data: data, kind: kind) - } - public func didReceiveMessage( connection: QuicConnection, stream: QuicStream, message: QuicMessage ) async { @@ -116,7 +95,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { // Call messageHandler safely in the actor context Task { [weak self] in guard let self else { return } - await messageHandler?.didReceiveMessage(messageID: messageID, message: message) + await messageHandler?.didReceiveMessage(server: self, messageID: messageID, message: message) } default: break diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index eaf24e53..c124b353 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -21,7 +21,7 @@ public class QuicStream: @unchecked Sendable { public private(set) var kind: StreamKind private weak var messageHandler: QuicStreamMessageHandler? private var streamCallback: StreamCallback? - private var sendCompletion: CheckedContinuation? + // private var sendCompletion: CheckedContinuation? // Initializer for creating a new stream init( api: UnsafePointer, connection: HQuic?, @@ -136,6 +136,7 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN + streamLogger.info("stream response flags: \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")") status = api.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw) if status.isFailed { streamLogger.error("StreamSend failed, \(status)!") @@ -147,51 +148,6 @@ public class QuicStream: @unchecked Sendable { } return status } - - // Sends data over the stream asynchronously and waits for the response - func send(data: Data, kind: StreamKind? = nil) async throws -> QuicMessage { - streamLogger.info("[\(String(describing: stream))] Sending data...") - var status = QuicStatusCode.success.rawValue - let messageLength = data.count - - let sendBufferRaw = UnsafeMutableRawPointer.allocate( - byteCount: MemoryLayout.size + messageLength, - alignment: MemoryLayout.alignment - ) - - let sendBuffer = sendBufferRaw.assumingMemoryBound(to: QuicBuffer.self) - let bufferPointer = UnsafeMutablePointer.allocate( - capacity: messageLength - ) - data.copyBytes(to: bufferPointer, count: messageLength) - - sendBuffer.pointee.Buffer = bufferPointer - sendBuffer.pointee.Length = UInt32(messageLength) - - // Use the provided kind if available, otherwise use the stream's kind - let effectiveKind = kind ?? self.kind - let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN - return try await withCheckedThrowingContinuation { [weak self] continuation in - guard let self else { - continuation.resume(throwing: QuicError.sendFailed) - return - } - sendCompletion = continuation - status = api.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw) - if status.isFailed { - streamLogger.error("StreamSend failed, \(status)!") - let shutdown: QuicStatus = - api.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0) - if shutdown.isFailed { - streamLogger.error( - "StreamShutdown failed, 0x\(String(format: "%x", shutdown))!" - ) - } - continuation.resume(throwing: QuicError.invalidStatus(status: status.code)) - sendCompletion = nil - } - } - } } extension QuicStream { @@ -223,17 +179,12 @@ extension QuicStream { let bufferData = Data(bytes: buffer.Buffer, count: bufferLength) receivedData.append(bufferData) } - + if event.pointee.RECEIVE.Flags.rawValue & QUIC_RECEIVE_FLAG_FIN.rawValue != 0 { + streamLogger.warning("[\(String(describing: stream))] FIN received in QUIC stream") + quicStream.messageHandler?.didReceiveMessage(quicStream, message: QuicMessage(type: .changeStreamType, data: nil)) + quicStream.kind = .commonEphemeral + } if receivedData.count > 0 { - if event.pointee.RECEIVE.Flags.rawValue & QUIC_RECEIVE_FLAG_FIN.rawValue != 0 { - streamLogger.warning("[\(String(describing: stream))] FIN received in QUIC stream") - quicStream.messageHandler?.didReceiveMessage(quicStream, message: QuicMessage(type: .changeStreamType, data: nil)) - quicStream.kind = .commonEphemeral - } - if let continuation = quicStream.sendCompletion { - continuation.resume(returning: QuicMessage(type: .received, data: receivedData)) - quicStream.sendCompletion = nil - } quicStream.messageHandler?.didReceiveMessage( quicStream, message: QuicMessage(type: .received, data: receivedData) ) @@ -254,11 +205,10 @@ extension QuicStream { case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: streamLogger.info("[\(String(describing: stream))] All done") if event.pointee.SHUTDOWN_COMPLETE.AppCloseInProgress == 0 { - quicStream.api.pointee.StreamClose(stream) - } - if let continuation = quicStream.sendCompletion { - continuation.resume(throwing: QuicError.sendFailed) - quicStream.sendCompletion = nil + if let stream = quicStream.stream { + streamLogger.info("[\(String(describing: stream))] Stream closed") + quicStream.api.pointee.StreamClose(stream) + } } default: diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index ea0bcf49..c320909e 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -91,45 +91,42 @@ final actor PeerTests { Task { do { for i in 1 ... 5 { - let messageToPeer2: QuicMessage = try await peer1.sendMessage( + let messageStatus2: QuicStatus = try await peer1.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4569), with: Message( data: Data("Hello from Peer1 - Message \(i)".utf8), type: PeerMessageType.commonEphemeral ) ) - print( - "Peer1 got message: \(String([UInt8](messageToPeer2.data!).map { Character(UnicodeScalar($0)) }))" - ) - let messageToPeer1: QuicMessage = try await peer2.sendMessage( + print("Peer1 sent message \(i): \(messageStatus2.isSucceeded ? "Success" : "Failed")") + + let messageStatus1: QuicStatus = try await peer2.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4568), with: Message( data: Data("Hello from Peer2 - Message \(i)".utf8), type: PeerMessageType.commonEphemeral ) ) - print( - "Peer2 got message: \(String([UInt8](messageToPeer1.data!).map { Character(UnicodeScalar($0)) }))" - ) + print("Peer2 sent message \(i): \(messageStatus1.isSucceeded ? "Success" : "Failed")") } for i in 6 ... 10 { - let messageToPeer2: QuicMessage = try await peer1.sendMessage( + let messageStatus2: QuicStatus = try await peer1.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4569), with: Message( data: Data("Hello from Peer1 - Message \(i)".utf8), type: PeerMessageType.uniquePersistent ) ) - print("Peer1 sent message \(i): \(messageToPeer2)") - let messageToPeer1: QuicMessage = try await peer2.sendMessage( + print("Peer1 sent message \(i): \(messageStatus2.isSucceeded ? "Success" : "Failed")") + let messageStatus1: QuicStatus = try await peer2.sendMessage( to: NetAddr(ipAddress: "127.0.0.1", port: 4568), with: Message( data: Data("Hello from Peer2 - Message \(i)".utf8), type: PeerMessageType.uniquePersistent ) ) - print("Peer2 sent message \(i): \(messageToPeer1)") + print("Peer2 sent message \(i): \(messageStatus1.isSucceeded ? "Success" : "Failed")") } } catch { print("Failed to send message: \(error)") diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index fdeb4af3..95259608 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -13,33 +13,31 @@ final class QuicClientTests { let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) let cert = Bundle.module.path(forResource: "server", ofType: "cert")! let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! + let quicServer = try await QuicServer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4565 + ), messageHandler: self + ) + try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() let quicClient = try await QuicClient( config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4563 + ipAddress: "127.0.0.1", port: 4565 ) ) - do { - let message1 = try await quicClient.send( - message: Data("Hello, World!".utf8), streamKind: .uniquePersistent - ) - print("Client received 1: \(message1)") - let message2 = try await quicClient.send( - message: Data("Hello, swift!".utf8), streamKind: .commonEphemeral - ) - print("Client received 2: \(message2)") - let message3 = try await quicClient.send( - message: Data("Hello, how are you!".utf8), streamKind: .uniquePersistent + + for i in 1 ... 10 { + let messageToPeer2: QuicStatus = try await quicClient.send( + data: Data("Hello from Client - Message \(i)".utf8), + streamKind: .uniquePersistent ) - print("Client received 3: \(message3)") - let message4 = try await quicClient.send( - message: Data("Hello, i am fine!".utf8), streamKind: .commonEphemeral + print("Client sent message \(i): \(messageToPeer2.isSucceeded ? "Success" : "Failed")") + let messageToPeer1: QuicStatus = try await quicClient.send( + data: Data("Hello from Client - Message \(i + 10)".utf8), + streamKind: .commonEphemeral ) - print("Client received 4: \(message4)") - - } catch { - // Handle the error if sending the message fails or if the connection fails - print("Failed about quic client: \(error)") + print("Client sent message \(i + 10): \(messageToPeer1.isSucceeded ? "Success" : "Failed")") } try await group.next().scheduleTask(in: .seconds(5)) { @@ -47,3 +45,27 @@ final class QuicClientTests { }.futureResult.get() } } + +extension QuicClientTests: QuicServerMessageHandler { + func didReceiveMessage(server: QuicServer, messageID: Int64, message: QuicMessage) async { + switch message.type { + case .received: + let messageString = String( + [UInt8](message.data!).map { Character(UnicodeScalar($0)) } + ) + print("Server received message : \(messageString)") + let status = await server.respondGetStatus(to: messageID, with: message.data!) + print("Server response message : \(status.isSucceeded ? "Success" : "Failed")") + case .shutdownComplete: + print("Server shutdown complete") + case .unknown: + print("Server unknown") + default: + break + } + } + + func didReceiveError(server _: QuicServer, messageID _: Int64, error: QuicError) async { + print("Server error: \(error)") + } +} diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index 8cc06dc1..880f58cc 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -22,10 +22,14 @@ final class QuicServerTests { } extension QuicServerTests: QuicServerMessageHandler { - func didReceiveMessage(messageID: Int64, message: QuicMessage) async { + func didReceiveMessage(server: QuicServer, messageID: Int64, message: QuicMessage) async { switch message.type { case .received: - print("Server received message with ID \(messageID): \(message)") + let messageString = String( + [UInt8](message.data!).map { Character(UnicodeScalar($0)) } + ) + print("Server received message : \(messageString)") + _ = await server.respondGetStatus(to: messageID, with: message.data!) case .shutdownComplete: print("Server shutdown complete") case .unknown: @@ -35,7 +39,7 @@ extension QuicServerTests: QuicServerMessageHandler { } } - func didReceiveError(messageID _: Int64, error: QuicError) async { + func didReceiveError(server _: QuicServer, messageID _: Int64, error: QuicError) async { print("Server error: \(error)") } } From 456562cb426da9c9ee1d01ac9038064a71514b01 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Mon, 7 Oct 2024 19:28:11 +0800 Subject: [PATCH 84/89] update quic stream --- .../Sources/Networking/msquic/QuicStream.swift | 12 ++++++++++-- .../Tests/NetworkingTests/QuicClientTest.swift | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index c124b353..de7fa9e6 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -181,14 +181,22 @@ extension QuicStream { } if event.pointee.RECEIVE.Flags.rawValue & QUIC_RECEIVE_FLAG_FIN.rawValue != 0 { streamLogger.warning("[\(String(describing: stream))] FIN received in QUIC stream") - quicStream.messageHandler?.didReceiveMessage(quicStream, message: QuicMessage(type: .changeStreamType, data: nil)) - quicStream.kind = .commonEphemeral +// quicStream.messageHandler?.didReceiveMessage(quicStream, message: QuicMessage(type: .changeStreamType, data: nil)) +// quicStream.kind = .commonEphemeral } + let messageString = String( + [UInt8](receivedData).map { Character(UnicodeScalar($0)) } + ) + streamLogger.info("[\(String(describing: stream))] RECEIVE message \(messageString)") + if receivedData.count > 0 { quicStream.messageHandler?.didReceiveMessage( quicStream, message: QuicMessage(type: .received, data: receivedData) ) } +// streamLogger.info("[\(String(describing: stream))] \(QuicStatus.pending.value)") + +// return QuicStatus.pending case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: streamLogger.warning("[\(String(describing: stream))] Peer send shutdown") diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index 95259608..60aec11c 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -30,7 +30,7 @@ final class QuicClientTests { for i in 1 ... 10 { let messageToPeer2: QuicStatus = try await quicClient.send( data: Data("Hello from Client - Message \(i)".utf8), - streamKind: .uniquePersistent + streamKind: .commonEphemeral ) print("Client sent message \(i): \(messageToPeer2.isSucceeded ? "Success" : "Failed")") let messageToPeer1: QuicStatus = try await quicClient.send( @@ -40,7 +40,7 @@ final class QuicClientTests { print("Client sent message \(i + 10): \(messageToPeer1.isSucceeded ? "Success" : "Failed")") } - try await group.next().scheduleTask(in: .seconds(5)) { + try await group.next().scheduleTask(in: .seconds(125)) { print("scheduleTask: 5s") }.futureResult.get() } From ce8f5427c8f3ed5dfea07c6b5a09fc57c057efa9 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 8 Oct 2024 15:15:09 +0800 Subject: [PATCH 85/89] update peer --- Networking/Sources/Networking/Peer.swift | 56 ++--------------- .../Networking/msquic/QuicClient.swift | 2 +- .../Networking/msquic/QuicConfig.swift | 4 +- .../Networking/msquic/QuicConnection.swift | 20 ++---- .../Networking/msquic/QuicListener.swift | 1 - .../Networking/msquic/QuicMessage.swift | 1 - .../Networking/msquic/QuicServer.swift | 13 ++-- .../Networking/msquic/QuicStream.swift | 29 +++------ .../NetworkingTests/QuicClientTest.swift | 62 ++++++++++++++----- .../NetworkingTests/QuicServerTest.swift | 4 +- 10 files changed, 75 insertions(+), 117 deletions(-) diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 5ca2783c..dab0b0ba 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -15,13 +15,13 @@ public protocol PeerMessage: Equatable, Sendable { } public struct PeerMessageReceived: Event { - public let messageID: Int64 + public let messageID: String public let message: QuicMessage } // TODO: add error or remove it public struct PeerErrorReceived: Event { - public let messageID: Int64? + public let messageID: String? public let error: QuicError } @@ -53,12 +53,12 @@ public actor Peer { } // Respond to a message with a specific messageID using Data - func respond(to messageID: Int64, with data: Data) async -> QuicStatus { + func respond(to messageID: String, with data: Data) async -> QuicStatus { await quicServer.respondGetStatus(to: messageID, with: data) } // Respond to a message with a specific messageID using PeerMessage - func respond(to messageID: Int64, with message: any PeerMessage) async -> QuicStatus { + func respond(to messageID: String, with message: any PeerMessage) async -> QuicStatus { let messageType = message.getMessageType() return await quicServer @@ -69,25 +69,6 @@ public actor Peer { ) } -// // Respond to a message with a specific messageID using PeerMessage (async throws) -// func respond(to messageID: Int64, with message: any PeerMessage) async throws { -// let messageType = message.getMessageType() -// let quicMessage = try await quicServer.respondGetMessage( -// to: messageID, with: message.getData(), -// kind: (messageType == .uniquePersistent) ? .uniquePersistent : .commonEphemeral -// ) -// if quicMessage.type != .received { -// throw QuicError.sendFailed -// } -// } - -// // Sends a message to another peer asynchronously -// func sendMessage(to peer: NetAddr, with message: any PeerMessage) async throws -> QuicMessage { -// let buffer = message.getData() -// let messageType = message.getMessageType() -// return try await sendDataToPeer(buffer, to: peer, messageType: messageType) -// } - // Sends a message to another peer and returns the status func sendMessage(to peer: NetAddr, with message: any PeerMessage) async throws -> QuicStatus { let buffer = message.getData() @@ -95,31 +76,6 @@ public actor Peer { return try await sendDataToPeer(buffer, to: peer, messageType: messageType) } -// // send message to other peer wait for response quicMessage -// private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) -// async throws -> QuicMessage -// { -// if let client = clients[peerAddr] { -// // Client already exists, use it to send the data -// return try await client.send( -// message: data, -// streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral -// ) -// } else { -// let config = QuicConfig( -// id: config.id, cert: config.cert, key: config.key, alpn: config.alpn, -// ipAddress: peerAddr.ipAddress, port: peerAddr.port -// ) -// // Client does not exist, create a new one -// let client = try await QuicClient(config: config, messageHandler: self) -// clients[peerAddr] = client -// return try await client.send( -// message: data, -// streamKind: messageType == .uniquePersistent ? .uniquePersistent : .commonEphemeral -// ) -// } -// } - private func sendDataToPeer(_ data: Data, to peerAddr: NetAddr, messageType: PeerMessageType) async throws -> QuicStatus { @@ -174,7 +130,7 @@ extension Peer: QuicClientMessageHandler { // QuicServerMessageHandler methods extension Peer: QuicServerMessageHandler { - public func didReceiveMessage(server _: QuicServer, messageID: Int64, message: QuicMessage) async { + public func didReceiveMessage(server _: QuicServer, messageID: String, message: QuicMessage) async { switch message.type { case .received: await eventBus.publish(PeerMessageReceived(messageID: messageID, message: message)) @@ -185,7 +141,7 @@ extension Peer: QuicServerMessageHandler { } } - public func didReceiveError(server _: QuicServer, messageID: Int64, error: QuicError) async { + public func didReceiveError(server _: QuicServer, messageID: String, error: QuicError) async { peerLogger.error("Failed to receive message: \(error)") await eventBus.publish(PeerErrorReceived(messageID: messageID, error: error)) } diff --git a/Networking/Sources/Networking/msquic/QuicClient.swift b/Networking/Sources/Networking/msquic/QuicClient.swift index 7cce2749..56f043a4 100644 --- a/Networking/Sources/Networking/msquic/QuicClient.swift +++ b/Networking/Sources/Networking/msquic/QuicClient.swift @@ -64,7 +64,7 @@ public actor QuicClient: Sendable, QuicConnectionMessageHandler { } else { try await connection.createCommonEphemeralStream() } - return sendStream.respond(with: data, kind: streamKind) + return sendStream.send(with: data, kind: streamKind) } func getNetAddr() -> NetAddr { diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index c7c3e6e9..35ce1663 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,11 +16,11 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 10000 + settings.IdleTimeoutMs = 30000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 - settings.PeerBidiStreamCount = 1 + settings.PeerBidiStreamCount = 100 settings.IsSet.PeerBidiStreamCount = 1 // Use withCString to avoid manual memory management diff --git a/Networking/Sources/Networking/msquic/QuicConnection.swift b/Networking/Sources/Networking/msquic/QuicConnection.swift index 7e3165a7..87b59251 100644 --- a/Networking/Sources/Networking/msquic/QuicConnection.swift +++ b/Networking/Sources/Networking/msquic/QuicConnection.swift @@ -56,12 +56,6 @@ actor StreamManager { } } - func changeTypeToCommon(_ stream: QuicStream) { - removeStream(stream) - stream.changeTypeToCommon() - addCommonStream(stream) - } - func closeAllCommonStreams() { for stream in commonStreams { stream.close() @@ -242,7 +236,6 @@ extension QuicConnection { stream: nil, message: QuicMessage(type: .shutdownComplete, data: nil) ) - quicConnection.messageHandler = nil } } @@ -279,12 +272,6 @@ extension QuicConnection: QuicStreamMessageHandler { stream.close() await streamManager.removeStream(stream) } - case .changeStreamType: - Task { - if stream.kind == .uniquePersistent { - await streamManager.changeTypeToCommon(stream) - } - } default: break } @@ -298,8 +285,11 @@ extension QuicConnection: QuicStreamMessageHandler { } // Handles errors received from the stream - public func didReceiveError(_: QuicStream, error: QuicError) { + public func didReceiveError(_ stream: QuicStream, error: QuicError) { logger.error("Failed to receive message: \(error)") - // await messageHandler?.didReceiveError(connection: self, stream: stream, error: error) + Task { [weak self] in + guard let self else { return } + await messageHandler?.didReceiveError(connection: self, stream: stream, error: error) + } } } diff --git a/Networking/Sources/Networking/msquic/QuicListener.swift b/Networking/Sources/Networking/msquic/QuicListener.swift index b838ae9a..2f937509 100644 --- a/Networking/Sources/Networking/msquic/QuicListener.swift +++ b/Networking/Sources/Networking/msquic/QuicListener.swift @@ -130,7 +130,6 @@ public class QuicListener: @unchecked Sendable { await listener.connectionsManager.add(quicConnection) } status = quicConnection.setCallbackHandler() - default: break } diff --git a/Networking/Sources/Networking/msquic/QuicMessage.swift b/Networking/Sources/Networking/msquic/QuicMessage.swift index 1248c862..3c4a2f94 100644 --- a/Networking/Sources/Networking/msquic/QuicMessage.swift +++ b/Networking/Sources/Networking/msquic/QuicMessage.swift @@ -6,7 +6,6 @@ enum QuicMessageType: String, Codable { case close case connected case shutdownComplete - case changeStreamType } public struct QuicMessage: Sendable, Equatable, Codable { diff --git a/Networking/Sources/Networking/msquic/QuicServer.swift b/Networking/Sources/Networking/msquic/QuicServer.swift index 89b45d1c..0e6657b2 100644 --- a/Networking/Sources/Networking/msquic/QuicServer.swift +++ b/Networking/Sources/Networking/msquic/QuicServer.swift @@ -6,8 +6,8 @@ import Utils let serverLogger: Logger = .init(label: "QuicServer") public protocol QuicServerMessageHandler: AnyObject, Sendable { - func didReceiveMessage(server: QuicServer, messageID: Int64, message: QuicMessage) async - func didReceiveError(server: QuicServer, messageID: Int64, error: QuicError) async + func didReceiveMessage(server: QuicServer, messageID: String, message: QuicMessage) async + func didReceiveError(server: QuicServer, messageID: String, error: QuicError) async } public actor QuicServer: Sendable, QuicListenerMessageHandler { @@ -17,7 +17,7 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { private var listener: QuicListener? private let config: QuicConfig private weak var messageHandler: QuicServerMessageHandler? - private var pendingMessages: [Int64: (QuicConnection, QuicStream)] + private var pendingMessages: [String: (QuicConnection, QuicStream)] init(config: QuicConfig, messageHandler: QuicServerMessageHandler? = nil) async throws { self.config = config @@ -70,14 +70,14 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { } // Respond to a message with a specific messageID using Data - func respondGetStatus(to messageID: Int64, with data: Data, kind: StreamKind? = nil) async + func respondGetStatus(to messageID: String, with data: Data, kind: StreamKind? = nil) async -> QuicStatus { var status = QuicStatusCode.internalError.rawValue if let (_, stream) = pendingMessages[messageID] { let streamKind = kind ?? stream.kind pendingMessages.removeValue(forKey: messageID) - status = stream.respond(with: data, kind: streamKind) + status = stream.send(with: data, kind: streamKind) } else { serverLogger.error("Message not found") } @@ -89,9 +89,8 @@ public actor QuicServer: Sendable, QuicListenerMessageHandler { ) async { switch message.type { case .received: - let messageID = Int64(Date().timeIntervalSince1970 * 1000) + let messageID = UUID().uuidString pendingMessages[messageID] = (connection, stream) - // Call messageHandler safely in the actor context Task { [weak self] in guard let self else { return } diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index de7fa9e6..11306d8c 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -18,7 +18,7 @@ public class QuicStream: @unchecked Sendable { private var stream: HQuic? private let api: UnsafePointer private let connection: HQuic? - public private(set) var kind: StreamKind + public let kind: StreamKind private weak var messageHandler: QuicStreamMessageHandler? private var streamCallback: StreamCallback? // private var sendCompletion: CheckedContinuation? @@ -93,10 +93,6 @@ public class QuicStream: @unchecked Sendable { streamLogger.debug("QuicStream close") } - func changeTypeToCommon() { - kind = .commonEphemeral - } - // Sets the callback handler for the stream func setCallbackHandler() { guard let stream else { @@ -114,8 +110,8 @@ public class QuicStream: @unchecked Sendable { ) } - func respond(with data: Data, kind: StreamKind? = nil) -> QuicStatus { - streamLogger.info("[\(String(describing: stream))] Respond data...") + func send(with data: Data, kind: StreamKind? = nil) -> QuicStatus { + streamLogger.info("[\(String(describing: stream))] Sending data...") var status = QuicStatusCode.success.rawValue let messageLength = data.count @@ -136,8 +132,8 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN - streamLogger.info("stream response flags: \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")") - status = api.pointee.StreamSend(stream, sendBuffer, 1, flags, sendBufferRaw) +// streamLogger.info("stream response flags: \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")") + status = api.pointee.StreamSend(stream, sendBuffer, 1, QUIC_SEND_FLAG_NONE, sendBufferRaw) if status.isFailed { streamLogger.error("StreamSend failed, \(status)!") let shutdown: QuicStatus = @@ -181,29 +177,18 @@ extension QuicStream { } if event.pointee.RECEIVE.Flags.rawValue & QUIC_RECEIVE_FLAG_FIN.rawValue != 0 { streamLogger.warning("[\(String(describing: stream))] FIN received in QUIC stream") -// quicStream.messageHandler?.didReceiveMessage(quicStream, message: QuicMessage(type: .changeStreamType, data: nil)) -// quicStream.kind = .commonEphemeral } - let messageString = String( - [UInt8](receivedData).map { Character(UnicodeScalar($0)) } - ) - streamLogger.info("[\(String(describing: stream))] RECEIVE message \(messageString)") if receivedData.count > 0 { quicStream.messageHandler?.didReceiveMessage( quicStream, message: QuicMessage(type: .received, data: receivedData) ) } -// streamLogger.info("[\(String(describing: stream))] \(QuicStatus.pending.value)") - -// return QuicStatus.pending case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: streamLogger.warning("[\(String(describing: stream))] Peer send shutdown") - if quicStream.kind == .uniquePersistent { - status = - quicStream.api.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0) - } + status = + quicStream.api.pointee.StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0) case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: streamLogger.error("[\(String(describing: stream))] Peer send aborted") diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index 60aec11c..2c89df0b 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -24,30 +24,57 @@ final class QuicClientTests { config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", ipAddress: "127.0.0.1", port: 4565 - ) + ), + messageHandler: self ) - for i in 1 ... 10 { - let messageToPeer2: QuicStatus = try await quicClient.send( - data: Data("Hello from Client - Message \(i)".utf8), - streamKind: .commonEphemeral - ) - print("Client sent message \(i): \(messageToPeer2.isSucceeded ? "Success" : "Failed")") - let messageToPeer1: QuicStatus = try await quicClient.send( - data: Data("Hello from Client - Message \(i + 10)".utf8), - streamKind: .commonEphemeral - ) - print("Client sent message \(i + 10): \(messageToPeer1.isSucceeded ? "Success" : "Failed")") - } + _ = try await group.next().scheduleTask(in: .seconds(2)) { + Task { + for i in 1 ... 10 { + let messageToPeer2: QuicStatus = try await quicClient.send( + data: Data("Hello from Client - Message \(i)".utf8), + streamKind: .commonEphemeral + ) + print("Client sent message \(i): \(messageToPeer2.isSucceeded ? "Success" : "Failed")") + let messageToPeer1: QuicStatus = try await quicClient.send( + data: Data("Hello from Client - Message \(i + 10)".utf8), + streamKind: .commonEphemeral + ) + print("Client sent message \(i + 10): \(messageToPeer1.isSucceeded ? "Success" : "Failed")") + } + } + }.futureResult.get() - try await group.next().scheduleTask(in: .seconds(125)) { + try await group.next().scheduleTask(in: .seconds(10)) { print("scheduleTask: 5s") }.futureResult.get() } } +extension QuicClientTests: QuicClientMessageHandler { + func didReceiveMessage(quicClient _: QuicClient, message: QuicMessage) async { + switch message.type { + case .received: + let messageString = String( + [UInt8](message.data!).map { Character(UnicodeScalar($0)) } + ) + print("Client received message : \(messageString)") + case .shutdownComplete: + print("Client shutdown complete") + case .unknown: + print("Client unknown") + default: + break + } + } + + func didReceiveError(quicClient _: QuicClient, error: QuicError) async { + print("Client error: \(error)") + } +} + extension QuicClientTests: QuicServerMessageHandler { - func didReceiveMessage(server: QuicServer, messageID: Int64, message: QuicMessage) async { + func didReceiveMessage(server: QuicServer, messageID: String, message: QuicMessage) async { switch message.type { case .received: let messageString = String( @@ -56,6 +83,9 @@ extension QuicClientTests: QuicServerMessageHandler { print("Server received message : \(messageString)") let status = await server.respondGetStatus(to: messageID, with: message.data!) print("Server response message : \(status.isSucceeded ? "Success" : "Failed")") + if status.isFailed { + print("Server response failed with messageID: \(messageID) \nmessage: \(messageString) ") + } case .shutdownComplete: print("Server shutdown complete") case .unknown: @@ -65,7 +95,7 @@ extension QuicClientTests: QuicServerMessageHandler { } } - func didReceiveError(server _: QuicServer, messageID _: Int64, error: QuicError) async { + func didReceiveError(server _: QuicServer, messageID _: String, error: QuicError) async { print("Server error: \(error)") } } diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift index 880f58cc..2bebc9f6 100644 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ b/Networking/Tests/NetworkingTests/QuicServerTest.swift @@ -22,7 +22,7 @@ final class QuicServerTests { } extension QuicServerTests: QuicServerMessageHandler { - func didReceiveMessage(server: QuicServer, messageID: Int64, message: QuicMessage) async { + func didReceiveMessage(server: QuicServer, messageID: String, message: QuicMessage) async { switch message.type { case .received: let messageString = String( @@ -39,7 +39,7 @@ extension QuicServerTests: QuicServerMessageHandler { } } - func didReceiveError(server _: QuicServer, messageID _: Int64, error: QuicError) async { + func didReceiveError(server _: QuicServer, messageID _: String, error: QuicError) async { print("Server error: \(error)") } } From 83f093abd87629435eb90cd43a2c3c1d23d2d936 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 8 Oct 2024 15:18:42 +0800 Subject: [PATCH 86/89] update stream --- Networking/Sources/Networking/msquic/QuicStream.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Networking/Sources/Networking/msquic/QuicStream.swift b/Networking/Sources/Networking/msquic/QuicStream.swift index 11306d8c..ac7d6916 100644 --- a/Networking/Sources/Networking/msquic/QuicStream.swift +++ b/Networking/Sources/Networking/msquic/QuicStream.swift @@ -132,7 +132,6 @@ public class QuicStream: @unchecked Sendable { // Use the provided kind if available, otherwise use the stream's kind let effectiveKind = kind ?? self.kind let flags = (effectiveKind == .uniquePersistent) ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN -// streamLogger.info("stream response flags: \((effectiveKind == .uniquePersistent) ? "QUIC_SEND_FLAG_NONE" : "QUIC_SEND_FLAG_FIN")") status = api.pointee.StreamSend(stream, sendBuffer, 1, QUIC_SEND_FLAG_NONE, sendBufferRaw) if status.isFailed { streamLogger.error("StreamSend failed, \(status)!") From 5e5bcdcc3d035a0996b1ff8825d9da3256c51427 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 8 Oct 2024 16:03:45 +0800 Subject: [PATCH 87/89] update test --- .../Sources/Networking/msquic/QuicConfig.swift | 2 +- .../Tests/NetworkingTests/QuicClientTest.swift | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Networking/Sources/Networking/msquic/QuicConfig.swift b/Networking/Sources/Networking/msquic/QuicConfig.swift index 35ce1663..6092549f 100644 --- a/Networking/Sources/Networking/msquic/QuicConfig.swift +++ b/Networking/Sources/Networking/msquic/QuicConfig.swift @@ -16,7 +16,7 @@ public struct QuicConfig: Sendable { ) throws { // Initialize QUIC settings var settings = QuicSettings() - settings.IdleTimeoutMs = 30000 + settings.IdleTimeoutMs = 10000 settings.IsSet.IdleTimeoutMs = 1 settings.ServerResumptionLevel = 2 // QUIC_SERVER_RESUME_AND_ZERORTT settings.IsSet.ServerResumptionLevel = 1 diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index 2c89df0b..fa4df795 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -13,12 +13,12 @@ final class QuicClientTests { let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) let cert = Bundle.module.path(forResource: "server", ofType: "cert")! let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! - let quicServer = try await QuicServer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4565 - ), messageHandler: self - ) +// let quicServer = try await QuicServer( +// config: QuicConfig( +// id: "public-key", cert: cert, key: keyFile, alpn: "sample", +// ipAddress: "127.0.0.1", port: 4565 +// ), messageHandler: self +// ) try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() let quicClient = try await QuicClient( config: QuicConfig( From 50c2bea981cfd62e44726e3f11c006f7086bfb84 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 8 Oct 2024 16:44:49 +0800 Subject: [PATCH 88/89] update quic client test --- .../Tests/NetworkingTests/QuicClientTest.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index fa4df795..02bfe3b0 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -10,16 +10,16 @@ import Testing #endif final class QuicClientTests { @Test func start() async throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) let cert = Bundle.module.path(forResource: "server", ofType: "cert")! let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! -// let quicServer = try await QuicServer( -// config: QuicConfig( -// id: "public-key", cert: cert, key: keyFile, alpn: "sample", -// ipAddress: "127.0.0.1", port: 4565 -// ), messageHandler: self -// ) - try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() + let quicServer = try await QuicServer( + config: QuicConfig( + id: "public-key", cert: cert, key: keyFile, alpn: "sample", + ipAddress: "127.0.0.1", port: 4565 + ), messageHandler: self + ) +// try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() let quicClient = try await QuicClient( config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", From 46b5d36234d11af65e5a813350b80229de1fea67 Mon Sep 17 00:00:00 2001 From: MacOMNI <414294494@qq.com> Date: Tue, 8 Oct 2024 17:34:36 +0800 Subject: [PATCH 89/89] udpate peer test --- .../Tests/NetworkingTests/PeerTest.swift | 2 +- .../NetworkingTests/QuicClientTest.swift | 20 ++++----- .../NetworkingTests/QuicServerTest.swift | 45 ------------------- 3 files changed, 10 insertions(+), 57 deletions(-) delete mode 100644 Networking/Tests/NetworkingTests/QuicServerTest.swift diff --git a/Networking/Tests/NetworkingTests/PeerTest.swift b/Networking/Tests/NetworkingTests/PeerTest.swift index c320909e..b63b8071 100644 --- a/Networking/Tests/NetworkingTests/PeerTest.swift +++ b/Networking/Tests/NetworkingTests/PeerTest.swift @@ -35,7 +35,7 @@ struct Message: PeerMessage { let cert = Bundle.module.path(forResource: "server", ofType: "cert")! let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! -final actor PeerTests { +final class PeerTests { @Test func testPeerCommunication() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) let eventBus1 = EventBus() diff --git a/Networking/Tests/NetworkingTests/QuicClientTest.swift b/Networking/Tests/NetworkingTests/QuicClientTest.swift index 02bfe3b0..3e862a87 100644 --- a/Networking/Tests/NetworkingTests/QuicClientTest.swift +++ b/Networking/Tests/NetworkingTests/QuicClientTest.swift @@ -10,20 +10,18 @@ import Testing #endif final class QuicClientTests { @Test func start() async throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let cert = Bundle.module.path(forResource: "server", ofType: "cert")! - let keyFile = Bundle.module.path(forResource: "server", ofType: "key")! - let quicServer = try await QuicServer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4565 - ), messageHandler: self - ) -// try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() + let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) +// let quicServer = try await QuicServer( +// config: QuicConfig( +// id: "public-key", cert: cert, key: keyFile, alpn: "sample", +// ipAddress: "127.0.0.1", port: 4563 +// ), messageHandler: self +// ) + try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() let quicClient = try await QuicClient( config: QuicConfig( id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4565 + ipAddress: "127.0.0.1", port: 4563 ), messageHandler: self ) diff --git a/Networking/Tests/NetworkingTests/QuicServerTest.swift b/Networking/Tests/NetworkingTests/QuicServerTest.swift deleted file mode 100644 index 2bebc9f6..00000000 --- a/Networking/Tests/NetworkingTests/QuicServerTest.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Foundation -import NIO -import Testing - -@testable import Networking - -#if os(macOS) - import CoreFoundation - import Security -#endif -final class QuicServerTests { - @Test func start() async throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let quicServer = try await QuicServer( - config: QuicConfig( - id: "public-key", cert: cert, key: keyFile, alpn: "sample", - ipAddress: "127.0.0.1", port: 4561 - ), messageHandler: self - ) - try await group.next().scheduleTask(in: .seconds(5)) {}.futureResult.get() - } -} - -extension QuicServerTests: QuicServerMessageHandler { - func didReceiveMessage(server: QuicServer, messageID: String, message: QuicMessage) async { - switch message.type { - case .received: - let messageString = String( - [UInt8](message.data!).map { Character(UnicodeScalar($0)) } - ) - print("Server received message : \(messageString)") - _ = await server.respondGetStatus(to: messageID, with: message.data!) - case .shutdownComplete: - print("Server shutdown complete") - case .unknown: - print("Server unknown") - default: - break - } - } - - func didReceiveError(server _: QuicServer, messageID _: String, error: QuicError) async { - print("Server error: \(error)") - } -}