// // GeminiConnection.swift // Gemini // // Created by Shadowfacts on 7/12/20. // import Foundation import Network protocol GeminiConnectionDelegate: class { func connectionReady(_ connection: GeminiConnection) func connection(_ connection: GeminiConnection, receivedData data: Data?, header: GeminiResponseHeader) func connectionCompleted(_ connection: GeminiConnection) } class GeminiConnection { weak var delegate: GeminiConnectionDelegate? private var connection: NWConnection? init(endpoint: NWEndpoint, delegate: GeminiConnectionDelegate? = nil) { self.connection = NWConnection(to: endpoint, using: .gemini) self.delegate = delegate startConnection() } private func startConnection() { guard let connection = connection else { return } connection.stateUpdateHandler = { (newState) in switch newState { case .ready: print("\(connection) established") self.delegate?.connectionReady(self) case let .failed(error): print("\(connection) failed: \(error)") connection.cancel() default: break } } // todo: should this be on a background queue? connection.start(queue: .main) } var report: NWConnection.PendingDataTransferReport! func sendRequest(_ request: GeminiRequest) { guard let connection = connection else { return } let message = NWProtocolFramer.Message(geminiRequest: request) let context = NWConnection.ContentContext(identifier: "GeminiRequest", metadata: [message]) // todo: should this really always be idempotent? connection.send(content: nil, contentContext: context, isComplete: true, completion: .contentProcessed({ (_) in })) receiveNextMessage() report = connection.startDataTransferReport() } private func receiveNextMessage() { guard let connection = connection else { return } // todo: should this really be .max connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { (data, context, isComplete, error) in // todo: handle error if let message = context?.protocolMetadata(definition: GeminiProtocol.definition) as? NWProtocolFramer.Message, let header = message.geminiResponseHeader, let delegate = self.delegate { delegate.connection(self, receivedData: data, header: header) self.receiveNextMessage() } if isComplete { self.delegate?.connectionCompleted(self) self.report.collect(queue: .main) { (report) in print(report.debugDescription) } // connection.cancel() } } // connection.receiveMessage { (data, context, isComplete, error) in // if let message = context?.protocolMetadata(definition: GeminiProtocol.definition) as? NWProtocolFramer.Message, // let header = message.geminiResponseHeader, // let delegate = self.delegate { //// let response = GeminiResponse(status: header.status, meta: header.meta, body: data) // delegate.connection(self, receivedData: data, header: header) // } // // todo: error handling // if let error = error { // print(error) // } else if isComplete { // self.delegate?.connectionCompleted(self) // connection.cancel() // } else { // self.receiveNextMessage() // } // } } }