diff options
| author | Dustin Mierau <dustin@mierau.me> | 2025-10-27 10:40:55 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2025-10-27 10:40:55 -0700 |
| commit | 2b278f662326b44d86d84ae3e11ff0b770757f45 (patch) | |
| tree | 241f62998ad1abaf45eaa228289a016d11c0068d /Hotline/Library | |
| parent | 26c6e0ade508f319ed995a1e6c5a53bf7c610c34 (diff) | |
Refactor HotlineFilePreviewClient to use NetSocketNew. Code is much cleaner.
Diffstat (limited to 'Hotline/Library')
| -rw-r--r-- | Hotline/Library/NetSocketNew.swift | 68 |
1 files changed, 56 insertions, 12 deletions
diff --git a/Hotline/Library/NetSocketNew.swift b/Hotline/Library/NetSocketNew.swift index 753a201..d4507b8 100644 --- a/Hotline/Library/NetSocketNew.swift +++ b/Hotline/Library/NetSocketNew.swift @@ -181,16 +181,17 @@ public actor NetSocketNew { /// Connect to a remote host and return a ready socket /// - /// This method establishes a TCP connection and waits until the connection is in `.ready` state. + /// This method establishes a TCP connection using Network framework types and waits until + /// the connection is in `.ready` state. /// /// - Parameters: - /// - host: Hostname or IP address to connect to - /// - port: Port number (0-65535) + /// - host: Network framework host (e.g., `.name("example.com", nil)` or `.ipv4(...)`) + /// - port: Network framework port /// - tls: TLS policy (default: enabled with default settings) /// - config: Socket configuration (default: standard settings) /// - Returns: A connected and ready `NetSocketNew` - /// - Throws: `NetSocketError.invalidPort` if port is invalid, or network errors - public static func connect(host: String, port: UInt16, tls: TLSPolicy = .enabled(), config: Config = .init()) async throws -> NetSocketNew { + /// - Throws: Network errors or connection failures + public static func connect(host: NWEndpoint.Host, port: NWEndpoint.Port, tls: TLSPolicy = .enabled(), config: Config = .init()) async throws -> NetSocketNew { let parameters = NWParameters.tcp if tls.enabled { let tlsOptions = NWProtocolTLS.Options() @@ -198,16 +199,20 @@ public actor NetSocketNew { parameters.defaultProtocolStack.applicationProtocols.insert(tlsOptions, at: 0) } - guard let nwPort = NWEndpoint.Port(rawValue: port) else { - throw NetSocketError.invalidPort - } - - let conn = NWConnection(host: .name(host, nil), port: nwPort, using: parameters) + let conn = NWConnection(host: host, port: port, using: parameters) let socket = NetSocketNew(connection: conn, config: config) try await socket.start() return socket } - + + /// Convenience wrapper to connect using string hostname and integer port + public static func connect(host: String, port: UInt16, tls: TLSPolicy = .enabled(), config: Config = .init()) async throws -> NetSocketNew { + guard let nwPort = NWEndpoint.Port(rawValue: port) else { + throw NetSocketError.invalidPort + } + return try await connect(host: .name(host, nil), port: nwPort, tls: tls, config: config) + } + /// Inject custom encoding/decoding logic (supports any encoder/decoder: JSON, CBOR, MessagePack, etc.) /// /// Example with JSONEncoder: @@ -738,7 +743,46 @@ public actor NetSocketNew { guard !isClosed else { throw NetSocketError.closed } } } - + + /// Read exactly N bytes with progress callbacks + /// + /// Like `read(_:)`, but reads in chunks and reports progress after each chunk. + /// Useful for downloading large amounts of data where you want to update UI progress. + /// + /// Example: + /// ```swift + /// let data = try await socket.read(1_000_000) { current, total in + /// print("Progress: \(current)/\(total)") + /// } + /// ``` + /// + /// - Parameters: + /// - count: Number of bytes to read + /// - chunkSize: Size of chunks to read at a time (default: 8192) + /// - progress: Optional callback with (bytesReceived, totalBytes) + /// - Returns: Exactly `count` bytes + /// - Throws: `NetSocketError` if connection closes before enough data arrives + public func read( + _ count: Int, + chunkSize: Int = 8192, + progress: (@Sendable (Int, Int) -> Void)? = nil + ) async throws -> Data { + var data = Data() + data.reserveCapacity(count) + var received = 0 + + while received < count { + try Task.checkCancellation() + let toRead = min(chunkSize, count - received) + let chunk = try await read(toRead) + data.append(chunk) + received += chunk.count + progress?(received, count) + } + + return data + } + func peek(_ count: Int) async throws -> Data { try await ensureReadable(count) let slice = buffer[head..<(head + count)] |