From afd9d5ae27d6f3cc01275286af5a9f46c54f75d0 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Mon, 5 Jun 2023 01:05:09 +0200 Subject: [PATCH 1/3] [M, F] refactoring --- ...ionError.swift => FKConnectionError.swift} | 10 +-- .../FusionKit/Model/FKConnectionFramer.swift | 83 +++++++++++++++++++ ...pcodes.swift => FKConnectionOpcodes.swift} | 8 +- .../FusionKit/Model/FKConnectionState.swift | 32 +++++++ .../FusionKit/Model/FNConnectionFrame.swift | 83 ------------------- .../FusionKit/Model/FNConnectionState.swift | 32 ------- ...{FNConnection.swift => FKConnection.swift} | 42 +++++----- ....swift => FKConnectionBytesProtocol.swift} | 4 +- ...swift => FKConnectionFramerProtocol.swift} | 8 +- .../Protocols/FKConnectionMessage.swift | 34 ++++++++ ...tocol.swift => FKConnectionProtocol.swift} | 14 ++-- .../Protocols/FNConnectionMessage.swift | 33 -------- Tests/FusionKitTests/FusionKitTests.swift | 28 +++---- 13 files changed, 206 insertions(+), 205 deletions(-) rename Sources/FusionKit/Error/{FNConnectionError.swift => FKConnectionError.swift} (83%) create mode 100644 Sources/FusionKit/Model/FKConnectionFramer.swift rename Sources/FusionKit/Model/{FNConnectionOpcodes.swift => FKConnectionOpcodes.swift} (71%) create mode 100644 Sources/FusionKit/Model/FKConnectionState.swift delete mode 100644 Sources/FusionKit/Model/FNConnectionFrame.swift delete mode 100644 Sources/FusionKit/Model/FNConnectionState.swift rename Sources/FusionKit/Network/{FNConnection.swift => FKConnection.swift} (82%) rename Sources/FusionKit/Protocols/{FNConnectionBytesProtocol.swift => FKConnectionBytesProtocol.swift} (77%) rename Sources/FusionKit/Protocols/{FNConnectionFrameProtocol.swift => FKConnectionFramerProtocol.swift} (75%) create mode 100644 Sources/FusionKit/Protocols/FKConnectionMessage.swift rename Sources/FusionKit/Protocols/{FNConnectionProtocol.swift => FKConnectionProtocol.swift} (69%) delete mode 100644 Sources/FusionKit/Protocols/FNConnectionMessage.swift diff --git a/Sources/FusionKit/Error/FNConnectionError.swift b/Sources/FusionKit/Error/FKConnectionError.swift similarity index 83% rename from Sources/FusionKit/Error/FNConnectionError.swift rename to Sources/FusionKit/Error/FKConnectionError.swift index c417987..502bd8f 100644 --- a/Sources/FusionKit/Error/FNConnectionError.swift +++ b/Sources/FusionKit/Error/FKConnectionError.swift @@ -1,5 +1,5 @@ // -// FNConnectionError.swift +// FKConnectionError.swift // FusionKit // // Created by Vinzenz Weist on 07.06.21. @@ -8,8 +8,8 @@ import Foundation -/// `FNConnection` specific errors -public enum FNConnectionError: Error { +/// The `FKConnection` specific errors +public enum FKConnectionError: Error { case missingHost case missingPort case connectionTimeout @@ -25,8 +25,8 @@ public enum FNConnectionError: Error { } } -/// `FNConnectionFrame` specific errors -public enum FNConnectionFrameError: Error { +/// The `FKConnectionFramer` specific errors +public enum FKConnectionFramerError: Error { case hashMismatch case parsingFailed case readBufferOverflow diff --git a/Sources/FusionKit/Model/FKConnectionFramer.swift b/Sources/FusionKit/Model/FKConnectionFramer.swift new file mode 100644 index 0000000..e3aa5a8 --- /dev/null +++ b/Sources/FusionKit/Model/FKConnectionFramer.swift @@ -0,0 +1,83 @@ +// +// FKConnectionFramer.swift +// FusionKit +// +// Created by Vinzenz Weist on 07.06.21. +// Copyright © 2021 Vinzenz Weist. All rights reserved. +// + +import Foundation +import CryptoKit + +internal final class FKConnectionFramer: FKConnectionFramerProtocol { + private var buffer = Data() + internal func reset() { buffer.removeAll() } + + /// Create a protocol conform message frame + /// + /// - Parameter message: generic type which conforms to 'Data' and 'String' + /// - Returns: generic Result type returning data and possible error + internal func create(message: T) -> Result { + guard message.raw.count <= FKConnectionNumbers.frame.rawValue - FKConnectionNumbers.overhead.rawValue else { return .failure(FKConnectionFramerError.writeBufferOverflow) } + var frame = Data() + frame.append(message.opcode) + frame.append(UInt32(message.raw.count + FKConnectionNumbers.overhead.rawValue).bigEndianBytes) + frame.append(Data(SHA256.hash(data: frame.prefix(FKConnectionNumbers.control.rawValue)))) + frame.append(message.raw) + return .success(frame) + } + + /// Parse a protocol conform message frame + /// + /// - Parameters: + /// - data: the data which should be parsed + /// - completion: completion block returns generic Result type with parsed message and possible error + internal func parse(data: Data, _ completion: (Result) -> Void) -> Void { + buffer.append(data) + guard let length = extractSize() else { return } + guard buffer.count <= FKConnectionNumbers.frame.rawValue else { completion(.failure(FKConnectionFramerError.readBufferOverflow)); return } + guard buffer.count >= FKConnectionNumbers.overhead.rawValue, buffer.count >= length else { return } + while buffer.count >= length && length != .zero { + guard SHA256.hash(data: buffer.prefix(FKConnectionNumbers.control.rawValue)) == extractHash() else { completion(.failure(FKConnectionFramerError.hashMismatch)); return } + guard let bytes = extractMessage() else { completion(.failure(FKConnectionFramerError.parsingFailed)); return } + switch buffer.first { + case FKConnectionOpcodes.binary.rawValue: completion(.success(bytes)) + case FKConnectionOpcodes.ping.rawValue: completion(.success(UInt16(bytes.count))) + case FKConnectionOpcodes.text.rawValue: guard let result = String(bytes: bytes, encoding: .utf8) else { return }; completion(.success(result)) + default: completion(.failure(FKConnectionFramerError.parsingFailed)) } + if buffer.count <= length { buffer.removeAll() } else { buffer = Data(buffer[length...]) } + } + } +} + +// MARK: - Private API Extension - + +private extension FKConnectionFramer { + /// Extract the message hash from the data, + /// if not possible it returns nil + /// - Returns: a `SHA256Digest` + private func extractHash() -> SHA256Digest? { + guard buffer.count >= FKConnectionNumbers.overhead.rawValue else { return nil } + let hash = buffer.subdata(in: FKConnectionNumbers.control.rawValue.. UInt32? { + guard buffer.count >= FKConnectionNumbers.overhead.rawValue else { return nil } + let size = buffer.subdata(in: FKConnectionNumbers.opcode.rawValue.. Data? { + guard buffer.count >= FKConnectionNumbers.overhead.rawValue else { return nil } + guard let length = extractSize() else { return nil } + guard length > FKConnectionNumbers.overhead.rawValue else { return Data() } + return buffer.subdata(in: FKConnectionNumbers.overhead.rawValue..(message: T) -> Result { - guard message.raw.count <= FNConnectionCounts.frame.rawValue - FNConnectionCounts.overhead.rawValue else { return .failure(FNConnectionFrameError.writeBufferOverflow) } - var frame = Data() - frame.append(message.opcode) - frame.append(UInt32(message.raw.count + FNConnectionCounts.overhead.rawValue).bigEndianBytes) - frame.append(Data(SHA256.hash(data: frame.prefix(FNConnectionCounts.control.rawValue)))) - frame.append(message.raw) - return .success(frame) - } - - /// Parse a protocol conform message frame - /// - /// - Parameters: - /// - data: the data which should be parsed - /// - completion: completion block returns generic Result type with parsed message and possible error - internal func parse(data: Data, _ completion: (Result) -> Void) -> Void { - buffer.append(data) - guard let length = extractSize() else { return } - guard buffer.count <= FNConnectionCounts.frame.rawValue else { completion(.failure(FNConnectionFrameError.readBufferOverflow)); return } - guard buffer.count >= FNConnectionCounts.overhead.rawValue, buffer.count >= length else { return } - while buffer.count >= length && length != .zero { - guard SHA256.hash(data: buffer.prefix(FNConnectionCounts.control.rawValue)) == extractHash() else { completion(.failure(FNConnectionFrameError.hashMismatch)); return } - guard let bytes = extractMessage() else { completion(.failure(FNConnectionFrameError.parsingFailed)); return } - switch buffer.first { - case FNConnectionOpcodes.binary.rawValue: completion(.success(bytes)) - case FNConnectionOpcodes.ping.rawValue: completion(.success(UInt16(bytes.count))) - case FNConnectionOpcodes.text.rawValue: guard let result = String(bytes: bytes, encoding: .utf8) else { return }; completion(.success(result)) - default: completion(.failure(FNConnectionFrameError.parsingFailed)) } - if buffer.count <= length { buffer.removeAll() } else { buffer = Data(buffer[length...]) } - } - } -} - -// MARK: - Private API Extension - - -private extension FNConnectionFrame { - /// Extract the message hash from the data, - /// if not possible it returns nil - /// - Returns: a `SHA256Digest` - private func extractHash() -> SHA256Digest? { - guard buffer.count >= FNConnectionCounts.overhead.rawValue else { return nil } - let hash = buffer.subdata(in: FNConnectionCounts.control.rawValue.. UInt32? { - guard buffer.count >= FNConnectionCounts.overhead.rawValue else { return nil } - let size = buffer.subdata(in: FNConnectionCounts.opcode.rawValue.. Data? { - guard buffer.count >= FNConnectionCounts.overhead.rawValue else { return nil } - guard let length = extractSize() else { return nil } - guard length > FNConnectionCounts.overhead.rawValue else { return Data() } - return buffer.subdata(in: FNConnectionCounts.overhead.rawValue.. Void = { _ in } +public final class FKConnection: FKConnectionProtocol { + public var stateUpdateHandler: (FKConnectionState) -> Void = { _ in } - private var transmitter: (FNConnectionTransmitter) -> Void = { _ in } - private var frame = FNConnectionFrame() - private let queue: DispatchQueue - private var connection: NWConnection - private var monitor = NWPathMonitor() + private var transmitter: (FKTransmitter) -> Void = { _ in } private var timer: DispatchSourceTimer? + private let framer = FKConnectionFramer() + private let queue: DispatchQueue + private let connection: NWConnection + private let monitor = NWPathMonitor() - /// The `FNConnection` is a custom Network protocol implementation of the Fusion Framing Protocol. + /// The `FKConnection` is a custom Network protocol implementation of the Fusion Framing Protocol. /// It's build on top of the `Network.framework` provided by Apple. A fast and lightweight Framing Protocol /// allows to transmit data as fast as possible and allows to measure a Networks's performance. /// @@ -29,8 +29,8 @@ public final class FNConnection: FNConnectionProtocol { /// - parameters: network parameters /// - queue: dispatch queue public required init(host: String, port: UInt16, parameters: NWParameters = .tcp, queue: DispatchQueue = .init(label: UUID().uuidString)) { - if host.isEmpty { fatalError(FNConnectionError.missingHost.description) } - if port == .zero { fatalError(FNConnectionError.missingPort.description) } + if host.isEmpty { fatalError(FKConnectionError.missingHost.description) } + if port == .zero { fatalError(FKConnectionError.missingPort.description) } self.connection = NWConnection(host: NWEndpoint.Host(host), port: NWEndpoint.Port(integerLiteral: port), using: parameters) self.queue = queue } @@ -48,10 +48,10 @@ public final class FNConnection: FNConnectionProtocol { /// Send messages to a connected host /// - Parameter message: generic type send `String`, `Data` and `UInt16` based messages - public func send(message: T) -> Void { + public func send(message: T) -> Void { queue.async { [weak self] in guard let self else { return } - let message = frame.create(message: message) + let message = framer.create(message: message) switch message { case .success(let data): let queued = data.chunks; if !queued.isEmpty { for data in queued { processing(with: data) } } case .failure(let error): stateUpdateHandler(.failed(error)); cleanup() } @@ -59,8 +59,8 @@ public final class FNConnection: FNConnectionProtocol { } /// Receive a message from a connected host - /// - Parameter completion: contains `FNConnectionMessage` and `FNConnectionBytes` generic message typ - public func receive(_ completion: @escaping (FNConnectionMessage?, FNConnectionBytes?) -> Void) -> Void { + /// - Parameter completion: contains `FKConnectionMessage` and `FKConnectionBytes` generic message typ + public func receive(_ completion: @escaping (FKConnectionMessage?, FKConnectionBytes?) -> Void) -> Void { transmitter = { result in switch result { case .message(let message): completion(message, nil) @@ -71,13 +71,13 @@ public final class FNConnection: FNConnectionProtocol { // MARK: - Private API - -private extension FNConnection { +private extension FKConnection { /// Start timeout and cancel connection, /// if timeout value is reached private func timeout() -> Void { timer = Timer.timeout { [weak self] in guard let self else { return } - cleanup(); stateUpdateHandler(.failed(FNConnectionError.connectionTimeout)) + cleanup(); stateUpdateHandler(.failed(FKConnectionError.connectionTimeout)) } } @@ -93,7 +93,7 @@ private extension FNConnection { guard let self else { return } switch path.status { case .satisfied: connection.start(queue: queue) - case .unsatisfied: cleanup(); stateUpdateHandler(.failed(FNConnectionError.connectionUnsatisfied)) + case .unsatisfied: cleanup(); stateUpdateHandler(.failed(FKConnectionError.connectionUnsatisfied)) default: break } } } @@ -104,7 +104,7 @@ private extension FNConnection { connection.batch { connection.send(content: data, completion: .contentProcessed { [weak self] error in guard let self else { return } - transmitter(.bytes(FNConnectionBytes(output: data.count))) + transmitter(.bytes(FKConnectionBytes(output: data.count))) guard let error, error != NWError.posix(.ECANCELED) else { return } stateUpdateHandler(.failed(error)) }) @@ -114,7 +114,7 @@ private extension FNConnection { /// Process message data and parse it into a conform message /// - Parameter data: message data private func processing(from data: Data) -> Void { - frame.parse(data: data) { [weak self] result in + framer.parse(data: data) { [weak self] result in guard let self else { return } switch result { case .success(let message): transmitter(.message(message)) @@ -130,7 +130,7 @@ private extension FNConnection { invalidate() monitor.cancel() connection.cancel() - frame.reset() + framer.reset() } } diff --git a/Sources/FusionKit/Protocols/FNConnectionBytesProtocol.swift b/Sources/FusionKit/Protocols/FKConnectionBytesProtocol.swift similarity index 77% rename from Sources/FusionKit/Protocols/FNConnectionBytesProtocol.swift rename to Sources/FusionKit/Protocols/FKConnectionBytesProtocol.swift index d3bdcb9..720f21c 100644 --- a/Sources/FusionKit/Protocols/FNConnectionBytesProtocol.swift +++ b/Sources/FusionKit/Protocols/FKConnectionBytesProtocol.swift @@ -1,5 +1,5 @@ // -// FNConnectionBytesProtocol.swift +// FKConnectionBytesProtocol.swift // FusionKit // // Created by Vinzenz Weist on 06.05.23. @@ -8,7 +8,7 @@ import Foundation -public protocol FNConnectionBytesProtocol { +internal protocol FKConnectionBytesProtocol { /// Input Bytes if available var input: Int? { get set } /// Output Bytes if available diff --git a/Sources/FusionKit/Protocols/FNConnectionFrameProtocol.swift b/Sources/FusionKit/Protocols/FKConnectionFramerProtocol.swift similarity index 75% rename from Sources/FusionKit/Protocols/FNConnectionFrameProtocol.swift rename to Sources/FusionKit/Protocols/FKConnectionFramerProtocol.swift index 95e785b..c513858 100644 --- a/Sources/FusionKit/Protocols/FNConnectionFrameProtocol.swift +++ b/Sources/FusionKit/Protocols/FKConnectionFramerProtocol.swift @@ -1,5 +1,5 @@ // -// FNConnectionFrameProtocol.swift +// FKConnectionFramerProtocol.swift // FusionKit // // Created by Vinzenz Weist on 07.06.21. @@ -8,17 +8,17 @@ import Foundation -internal protocol FNConnectionFrameProtocol { +internal protocol FKConnectionFramerProtocol { /// Create a protocol conform message frame /// /// - Parameter message: generic type which conforms to 'Data' and 'String' /// - Returns: generic Result type returning data and possible error - func create(message: T) -> Result + func create(message: T) -> Result /// Parse a protocol conform message frame /// /// - Parameters: /// - data: the data which should be parsed /// - completion: completion block returns generic Result type with parsed message and possible error - func parse(data: Data, _ completion: (Result) -> Void) -> Void + func parse(data: Data, _ completion: (Result) -> Void) -> Void } diff --git a/Sources/FusionKit/Protocols/FKConnectionMessage.swift b/Sources/FusionKit/Protocols/FKConnectionMessage.swift new file mode 100644 index 0000000..1d5ec4b --- /dev/null +++ b/Sources/FusionKit/Protocols/FKConnectionMessage.swift @@ -0,0 +1,34 @@ +// +// FKConnectionMessage.swift +// FusionKit +// +// Created by Vinzenz Weist on 07.06.21. +// Copyright © 2021 Vinzenz Weist. All rights reserved. +// + +import Foundation +import StoreKit + +/// Protocol for message compliance +public protocol FKConnectionMessage { + var opcode: UInt8 { get } + var raw: Data { get } +} + +/// Conformance to protocol 'FKConnectionMessage' +extension UInt16: FKConnectionMessage { + public var opcode: UInt8 { FKConnectionOpcodes.ping.rawValue } + public var raw: Data { Data(count: Int(self)) } +} + +/// Conformance to protocol 'FKConnectionMessage' +extension String: FKConnectionMessage { + public var opcode: UInt8 { FKConnectionOpcodes.text.rawValue } + public var raw: Data { Data(self.utf8) } +} + +/// Conformance to protocol 'FKConnectionMessage' +extension Data: FKConnectionMessage { + public var opcode: UInt8 { FKConnectionOpcodes.binary.rawValue } + public var raw: Data { self } +} diff --git a/Sources/FusionKit/Protocols/FNConnectionProtocol.swift b/Sources/FusionKit/Protocols/FKConnectionProtocol.swift similarity index 69% rename from Sources/FusionKit/Protocols/FNConnectionProtocol.swift rename to Sources/FusionKit/Protocols/FKConnectionProtocol.swift index 72b090f..25953f0 100644 --- a/Sources/FusionKit/Protocols/FNConnectionProtocol.swift +++ b/Sources/FusionKit/Protocols/FKConnectionProtocol.swift @@ -1,5 +1,5 @@ // -// FNConnectionProtocol.swift +// FKConnectionProtocol.swift // FusionKit // // Created by Vinzenz Weist on 07.06.21. @@ -9,11 +9,11 @@ import Foundation import Network -public protocol FNConnectionProtocol { +internal protocol FKConnectionProtocol { /// Access to connection State's - var stateUpdateHandler: (FNConnectionState) -> Void { get set } + var stateUpdateHandler: (FKConnectionState) -> Void { get set } - /// The `FNConnection` is a custom Network protocol implementation of the Fusion Framing Protocol. + /// The `FKConnection` is a custom Network protocol implementation of the Fusion Framing Protocol. /// It's build on top of the `Network.framework` provided by Apple. A fast and lightweight Framing Protocol /// allows to transmit data as fast as possible and allows to measure a Networks's performance. /// @@ -32,9 +32,9 @@ public protocol FNConnectionProtocol { /// Send messages to a connected host /// - Parameter message: generic type send `String`, `Data` and `UInt16` based messages - func send(message: T) -> Void + func send(message: T) -> Void /// Receive a message from a connected host - /// - Parameter completion: contains `FNConnectionMessage` and `FNConnectionBytes` generic message typ - func receive(_ completion: @escaping (FNConnectionMessage?, FNConnectionBytes?) -> Void) -> Void + /// - Parameter completion: contains `FKConnectionMessage` and `FKConnectionBytes` generic message typ + func receive(_ completion: @escaping (FKConnectionMessage?, FKConnectionBytes?) -> Void) -> Void } diff --git a/Sources/FusionKit/Protocols/FNConnectionMessage.swift b/Sources/FusionKit/Protocols/FNConnectionMessage.swift deleted file mode 100644 index 8929373..0000000 --- a/Sources/FusionKit/Protocols/FNConnectionMessage.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// FNConnectionMessage.swift -// FusionKit -// -// Created by Vinzenz Weist on 07.06.21. -// Copyright © 2021 Vinzenz Weist. All rights reserved. -// - -import Foundation - -/// Protocol for message compliance -public protocol FNConnectionMessage { - var opcode: UInt8 { get } - var raw: Data { get } -} - -/// Conformance to protocol 'FNConnectionMessage' -extension UInt16: FNConnectionMessage { - public var opcode: UInt8 { FNConnectionOpcodes.ping.rawValue } - public var raw: Data { Data(count: Int(self)) } -} - -/// Conformance to protocol 'FNConnectionMessage' -extension String: FNConnectionMessage { - public var opcode: UInt8 { FNConnectionOpcodes.text.rawValue } - public var raw: Data { Data(self.utf8) } -} - -/// Conformance to protocol 'FNConnectionMessage' -extension Data: FNConnectionMessage { - public var opcode: UInt8 { FNConnectionOpcodes.binary.rawValue } - public var raw: Data { self } -} diff --git a/Tests/FusionKitTests/FusionKitTests.swift b/Tests/FusionKitTests/FusionKitTests.swift index 7f48b23..ec47591 100644 --- a/Tests/FusionKitTests/FusionKitTests.swift +++ b/Tests/FusionKitTests/FusionKitTests.swift @@ -16,7 +16,7 @@ private enum TestCase { } class FusionKitTests: XCTestCase { - private var connection = FNConnection(host: "atonet.de", port: 7878) + private var connection = FKConnection(host: "fusion.atonet.de", port: 7878) private var buffer = "50000" private let timeout = 10.0 private let uuid = UUID().uuidString @@ -56,15 +56,15 @@ class FusionKitTests: XCTestCase { /// Start test error description mapping func testErrorDescription() { - XCTAssertEqual(FNConnectionError.missingHost.description, "missing host") - XCTAssertEqual(FNConnectionError.missingPort.description, "missing port") - XCTAssertEqual(FNConnectionError.connectionTimeout.description, "connection timeout") - XCTAssertEqual(FNConnectionError.connectionUnsatisfied.description, "connection path is not satisfied") + XCTAssertEqual(FKConnectionError.missingHost.description, "missing host") + XCTAssertEqual(FKConnectionError.missingPort.description, "missing port") + XCTAssertEqual(FKConnectionError.connectionTimeout.description, "connection timeout") + XCTAssertEqual(FKConnectionError.connectionUnsatisfied.description, "connection path is not satisfied") - XCTAssertEqual(FNConnectionFrameError.hashMismatch.description, "message hash does not match") - XCTAssertEqual(FNConnectionFrameError.parsingFailed.description, "message parsing failed") - XCTAssertEqual(FNConnectionFrameError.readBufferOverflow.description, "read buffer overflow") - XCTAssertEqual(FNConnectionFrameError.writeBufferOverflow.description, "write buffer overflow") + XCTAssertEqual(FKConnectionFramerError.hashMismatch.description, "message hash does not match") + XCTAssertEqual(FKConnectionFramerError.parsingFailed.description, "message parsing failed") + XCTAssertEqual(FKConnectionFramerError.readBufferOverflow.description, "read buffer overflow") + XCTAssertEqual(FKConnectionFramerError.writeBufferOverflow.description, "write buffer overflow") exp.fulfill() wait(for: [exp], timeout: timeout) @@ -87,8 +87,8 @@ private extension FusionKitTests { } /// Message framer - private func framer(message: T) { - let framer = FNConnectionFrame() + private func framer(message: T) { + let framer = FKConnectionFramer() let message = framer.create(message: message) switch message { case .success(let data): @@ -104,8 +104,8 @@ private extension FusionKitTests { } /// Handles test routes for messages - /// - Parameter message: generic `FNConnectionMessage` - private func handleMessages(message: FNConnectionMessage) { + /// - Parameter message: generic `FKConnectionMessage` + private func handleMessages(message: FKConnectionMessage) { if case let message as UInt16 = message { XCTAssertEqual(message, UInt16(buffer)) connection.cancel() @@ -125,7 +125,7 @@ private extension FusionKitTests { /// State update handler for connection /// - Parameter connection: instance of 'NetworkConnection' - private func stateUpdateHandler(connection: FNConnection, test: TestCase) { + private func stateUpdateHandler(connection: FKConnection, test: TestCase) { connection.stateUpdateHandler = { [weak self] state in guard let self else { return } switch state { From 4a0a5e4f0e3413c793b0c82b9b2bea72f06799fe Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Mon, 5 Jun 2023 01:13:29 +0200 Subject: [PATCH 2/3] [F] reduced error classes --- .../FusionKit/Error/FKConnectionError.swift | 20 +++++-------------- .../FusionKit/Model/FKConnectionFramer.swift | 10 +++++----- Tests/FusionKitTests/FusionKitTests.swift | 9 ++++----- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Sources/FusionKit/Error/FKConnectionError.swift b/Sources/FusionKit/Error/FKConnectionError.swift index 502bd8f..9b08d21 100644 --- a/Sources/FusionKit/Error/FKConnectionError.swift +++ b/Sources/FusionKit/Error/FKConnectionError.swift @@ -14,19 +14,6 @@ public enum FKConnectionError: Error { case missingPort case connectionTimeout case connectionUnsatisfied - - public var description: String { - switch self { - case .missingHost: return "missing host" - case .missingPort: return "missing port" - case .connectionTimeout: return "connection timeout" - case .connectionUnsatisfied: return "connection path is not satisfied" - } - } -} - -/// The `FKConnectionFramer` specific errors -public enum FKConnectionFramerError: Error { case hashMismatch case parsingFailed case readBufferOverflow @@ -34,10 +21,13 @@ public enum FKConnectionFramerError: Error { public var description: String { switch self { + case .missingHost: return "missing host" + case .missingPort: return "missing port" + case .connectionTimeout: return "connection timeout" + case .connectionUnsatisfied: return "connection path is not satisfied" case .hashMismatch: return "message hash does not match" case .parsingFailed: return "message parsing failed" case .readBufferOverflow: return "read buffer overflow" - case .writeBufferOverflow: return "write buffer overflow" - } + case .writeBufferOverflow: return "write buffer overflow" } } } diff --git a/Sources/FusionKit/Model/FKConnectionFramer.swift b/Sources/FusionKit/Model/FKConnectionFramer.swift index e3aa5a8..8a84530 100644 --- a/Sources/FusionKit/Model/FKConnectionFramer.swift +++ b/Sources/FusionKit/Model/FKConnectionFramer.swift @@ -18,7 +18,7 @@ internal final class FKConnectionFramer: FKConnectionFramerProtocol { /// - Parameter message: generic type which conforms to 'Data' and 'String' /// - Returns: generic Result type returning data and possible error internal func create(message: T) -> Result { - guard message.raw.count <= FKConnectionNumbers.frame.rawValue - FKConnectionNumbers.overhead.rawValue else { return .failure(FKConnectionFramerError.writeBufferOverflow) } + guard message.raw.count <= FKConnectionNumbers.frame.rawValue - FKConnectionNumbers.overhead.rawValue else { return .failure(FKConnectionError.writeBufferOverflow) } var frame = Data() frame.append(message.opcode) frame.append(UInt32(message.raw.count + FKConnectionNumbers.overhead.rawValue).bigEndianBytes) @@ -35,16 +35,16 @@ internal final class FKConnectionFramer: FKConnectionFramerProtocol { internal func parse(data: Data, _ completion: (Result) -> Void) -> Void { buffer.append(data) guard let length = extractSize() else { return } - guard buffer.count <= FKConnectionNumbers.frame.rawValue else { completion(.failure(FKConnectionFramerError.readBufferOverflow)); return } + guard buffer.count <= FKConnectionNumbers.frame.rawValue else { completion(.failure(FKConnectionError.readBufferOverflow)); return } guard buffer.count >= FKConnectionNumbers.overhead.rawValue, buffer.count >= length else { return } while buffer.count >= length && length != .zero { - guard SHA256.hash(data: buffer.prefix(FKConnectionNumbers.control.rawValue)) == extractHash() else { completion(.failure(FKConnectionFramerError.hashMismatch)); return } - guard let bytes = extractMessage() else { completion(.failure(FKConnectionFramerError.parsingFailed)); return } + guard SHA256.hash(data: buffer.prefix(FKConnectionNumbers.control.rawValue)) == extractHash() else { completion(.failure(FKConnectionError.hashMismatch)); return } + guard let bytes = extractMessage() else { completion(.failure(FKConnectionError.parsingFailed)); return } switch buffer.first { case FKConnectionOpcodes.binary.rawValue: completion(.success(bytes)) case FKConnectionOpcodes.ping.rawValue: completion(.success(UInt16(bytes.count))) case FKConnectionOpcodes.text.rawValue: guard let result = String(bytes: bytes, encoding: .utf8) else { return }; completion(.success(result)) - default: completion(.failure(FKConnectionFramerError.parsingFailed)) } + default: completion(.failure(FKConnectionError.parsingFailed)) } if buffer.count <= length { buffer.removeAll() } else { buffer = Data(buffer[length...]) } } } diff --git a/Tests/FusionKitTests/FusionKitTests.swift b/Tests/FusionKitTests/FusionKitTests.swift index ec47591..69b88b6 100644 --- a/Tests/FusionKitTests/FusionKitTests.swift +++ b/Tests/FusionKitTests/FusionKitTests.swift @@ -60,11 +60,10 @@ class FusionKitTests: XCTestCase { XCTAssertEqual(FKConnectionError.missingPort.description, "missing port") XCTAssertEqual(FKConnectionError.connectionTimeout.description, "connection timeout") XCTAssertEqual(FKConnectionError.connectionUnsatisfied.description, "connection path is not satisfied") - - XCTAssertEqual(FKConnectionFramerError.hashMismatch.description, "message hash does not match") - XCTAssertEqual(FKConnectionFramerError.parsingFailed.description, "message parsing failed") - XCTAssertEqual(FKConnectionFramerError.readBufferOverflow.description, "read buffer overflow") - XCTAssertEqual(FKConnectionFramerError.writeBufferOverflow.description, "write buffer overflow") + XCTAssertEqual(FKConnectionError.hashMismatch.description, "message hash does not match") + XCTAssertEqual(FKConnectionError.parsingFailed.description, "message parsing failed") + XCTAssertEqual(FKConnectionError.readBufferOverflow.description, "read buffer overflow") + XCTAssertEqual(FKConnectionError.writeBufferOverflow.description, "write buffer overflow") exp.fulfill() wait(for: [exp], timeout: timeout) From ed06fc6b48cedfc4315d43b370d650c7363c66ef Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Mon, 5 Jun 2023 02:48:35 +0200 Subject: [PATCH 3/3] [M] minor changes --- README.md | 10 +++++----- Sources/FusionKit/Model/FKConnectionOpcodes.swift | 2 +- Sources/FusionKit/Network/FKConnection.swift | 4 ++-- Sources/FusionKit/Protocols/FKConnectionProtocol.swift | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4a1f6af..51ad7c3 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ dependencies: [ import FusionKit // create a new connection -let connection = FNConnection(host: "example.com", port: 8080) +let connection = FNKonnection(host: "example.com", port: 8080) // support for NWParameters, tls example: -let connection = FNConnection(host: "example.com", port: 8080, parameters: .tls) +let connection = FNKonnection(host: "example.com", port: 8080, parameters: .tls) // ... ``` @@ -42,7 +42,7 @@ let connection = FNConnection(host: "example.com", port: 8080, parameters: .tls) import FusionKit // create a new connection -let connection = FNConnection(host: "example.com", port: 8080) +let connection = FNKonnection(host: "example.com", port: 8080) // state update handler connection.stateUpdateHandler = { state in @@ -66,7 +66,7 @@ connection.start() import FusionKit // create a new connection -let connection = FNConnection(host: "example.com", port: 8080) +let connection = FNKonnection(host: "example.com", port: 8080) // the framework accepts generic data types // send strings @@ -85,7 +85,7 @@ connection.send(message: UInt16.max) import FusionKit // create a new connection -let connection = FNConnection(host: "example.com", port: 8080) +let connection = FNKonnection(host: "example.com", port: 8080) // read incoming messages and transmitted bytes count connection.receive { message, bytes in diff --git a/Sources/FusionKit/Model/FKConnectionOpcodes.swift b/Sources/FusionKit/Model/FKConnectionOpcodes.swift index df2b3c3..b184627 100644 --- a/Sources/FusionKit/Model/FKConnectionOpcodes.swift +++ b/Sources/FusionKit/Model/FKConnectionOpcodes.swift @@ -16,7 +16,7 @@ internal enum FKConnectionOpcodes: UInt8 { case ping = 0x3 } -// Protocol byte numbers +/// Protocol byte numbers internal enum FKConnectionNumbers: Int { case opcode = 0x1 case control = 0x5 diff --git a/Sources/FusionKit/Network/FKConnection.swift b/Sources/FusionKit/Network/FKConnection.swift index 00c4ad7..2309763 100644 --- a/Sources/FusionKit/Network/FKConnection.swift +++ b/Sources/FusionKit/Network/FKConnection.swift @@ -14,8 +14,8 @@ public final class FKConnection: FKConnectionProtocol { private var transmitter: (FKTransmitter) -> Void = { _ in } private var timer: DispatchSourceTimer? - private let framer = FKConnectionFramer() private let queue: DispatchQueue + private let framer = FKConnectionFramer() private let connection: NWConnection private let monitor = NWPathMonitor() @@ -35,7 +35,7 @@ public final class FKConnection: FKConnectionProtocol { self.queue = queue } - /// Start a connecting to a host + /// Start a connection public func start() -> Void { timeout(); handler(); receive(); satisfied() monitor.start(queue: queue) diff --git a/Sources/FusionKit/Protocols/FKConnectionProtocol.swift b/Sources/FusionKit/Protocols/FKConnectionProtocol.swift index 25953f0..cfcf496 100644 --- a/Sources/FusionKit/Protocols/FKConnectionProtocol.swift +++ b/Sources/FusionKit/Protocols/FKConnectionProtocol.swift @@ -24,7 +24,7 @@ internal protocol FKConnectionProtocol { /// - queue: dispatch queue init(host: String, port: UInt16, parameters: NWParameters, queue: DispatchQueue) - /// Start a connecting to a host + /// Start a connection func start() -> Void /// Cancel the current connection