Posts

Post not yet marked as solved
2 Replies
855 Views
Working on an MQTT client. We have a Network.framework solution, but that only supports macOS 10.14+. We need to support 10.13+. We have some code for an NSStream-based solution, but that seems to support only TLSv1, not to mention that a WWDC18 session 'highly discouraged' its use. So, we are attempting to use URLSessionStreamTask to connect and capture the streams which are then handed back to the NSStream-based code. Yes, we could just rewrite more of the code, add our own async read, etc,. but... Anyway, the modified code to use the URLSessionStreamTask has few changes: the streams are obtained by creating a URLSession & streamTask, then calling the task's captureStreams() method. An extension to the class was added to implement the URLSessionStreamDelegate; existing code was moved into the callback method to handle stream setup.A URLSession is created & successfully connects (we are testing against a mosquitto server which has an insecure cert, but URLSessionStreamDelegate handles the challenge) private var inputStream: InputStream? = nil private var outputStream: OutputStream? = nil private var sessionQueue: DispatchQueue private var session: URLSession? = nil private var streamTask: URLSessionStreamTask? = nil ... session = URLSession(configuration: URLSessionConfiguration.default, delegate: self as URLSessionStreamDelegate, delegateQueue: nil) streamTask = session!.streamTask(withHostName: host, port: port) if secure { streamTask!.startSecureConnection() } streamTask!.captureStreams() streamTask!.resume() ...The delegate is called with the captured streams which are 'attached' to the existing code extension MQTTSessionStream: URLSessionStreamDelegate { func urlSession(_ session: URLSession, streamTask: URLSessionStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream) { //setup the streams self.inputStream = inputStream self.inputStream!.delegate = self as StreamDelegate self.outputStream = outputStream self.outputStream!.delegate = self as StreamDelegate self.sessionQueue.async { [weak self] in guard let `self` = self else { return } self.currentRunLoop = RunLoop.current self.inputStream!.schedule(in: self.currentRunLoop!, forMode: RunLoop.Mode.default) self.outputStream!.schedule(in: self.currentRunLoop!, forMode: RunLoop.Mode.default) let timeout: TimeInterval = 30 //- temporary - later, make this a member variable and set in init... if timeout > 0 { DispatchQueue.global().asyncAfter(deadline: .now() + timeout) { self.connectTimeout() } } self.inputStream!.open() self.inputReady = true; self.outputStream!.open() self.outputReady = true; self.currentRunLoop!.run() } } func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { ... } }Log lines:2020-01-24 09:56:48.184007-0700 0x149a66 Default 0x0 13146 0 stmqtt: (CFNetwork) Connection 1: connected successfully2020-01-24 09:56:48.184046-0700 0x149a66 Default 0x0 13146 0 stmqtt: (CFNetwork) Connection 1: TLS handshake complete2020-01-24 09:56:48.184296-0700 0x149a66 Default 0x0 13146 0 stmqtt: (CFNetwork) Connection 1: ready C(N) E(N)2020-01-24 09:56:48.184320-0700 0x149a66 Default 0x0 13146 0 stmqtt: (CFNetwork) StreamTask <99FB193B-B74A-4BE1-B6BA-EBBAA6C1C315>.<0> is complete and received server trust, marking as secure2020-01-24 09:56:48.184390-0700 0x149a66 Default 0x0 13146 0 stmqtt: (CFNetwork) Establish Object : 0x7ffb16d08dc8 .Task : 0x7ffb189088d02020-01-24 09:56:48.184411-0700 0x149a66 Default 0x0 13146 0 stmqtt: (CFNetwork) Connection 1: received viability advisory(Y)2020-01-24 09:56:54.442740-0700 0x149ad6 Activity 0x1103d4 13146 0 stmqtt: (Security) SecKeychainAddCallback2020-01-24 09:58:13.850514-0700 0x149d3e Default 0x0 13146 0 stmqtt: (libboringssl.dylib) [com.apple.network.boringssl:BoringSSL] boringssl_context_message_handler(2259) [C1.1:2][0x7ffb1890b240] Reading SSL3_RT_ALERT 2 bytesHowever, the StreamDelegate method to handle the events is never called and so the stream is soon closed.extension MQTTSessionStream: StreamDelegate { @objc internal func stream(_ aStream: Stream, handle eventCode: Stream.Event) { switch eventCode { case Stream.Event.openCompleted: ... case Stream.Event.hasBytesAvailable: ... case Stream.Event.errorOccurred: ... case Stream.Event.endEncountered: ... case Stream.Event.hasSpaceAvailable: ... default: break } }}Log lines:2020-01-24 09:58:13.850554-0700 0x149d3e Default 0x0 13146 0 stmqtt: (libboringssl.dylib) [com.apple.network.boringssl:BoringSSL] boringssl_context_handle_warning_alert(1894) [C1.1:2][0x7ffb1890b240] read alert, level: warning, description: close notify2020-01-24 09:58:13.850571-0700 0x149d3e Default 0x0 13146 0 stmqtt: (libboringssl.dylib) [com.apple.network.boringssl:BoringSSL] nw_protocol_boringssl_input_finished(1676) [C1.1:2][0x7ffb1890b240] state: 22020-01-24 09:58:13.850607-0700 0x149d3e Default 0x0 13146 0 stmqtt: (CFNetwork) Connection 1: read-side closed2020-01-24 09:58:13.850621-0700 0x149d3e Error 0x0 13146 0 stmqtt: (CFNetwork) Connection 1: missing error, so heuristics synthesized error(1:53)2020-01-24 09:58:13.850631-0700 0x149d3e Error 0x0 13146 0 stmqtt: (CFNetwork) Connection 1: encountered error(1:53)The WWDC18 session in which the URLSessionStreamTask was presented indicated that once the streams were captured, they could be used in code that previously had used NSStreams. Did I miss some other required setup? Does anything seem obviously wrong with
Posted Last updated
.