"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-7305/ios/app/broadcast-extension/SocketConnection.swift" (26 May 2023, 6077 Bytes) of package /linux/misc/jitsi-meet-7305.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Swift source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * Copyright @ 2021-present 8x8, Inc.
    3  *
    4  * Licensed under the Apache License, Version 2.0 (the "License");
    5  * you may not use this file except in compliance with the License.
    6  * You may obtain a copy of the License at
    7  *
    8  *     http://www.apache.org/licenses/LICENSE-2.0
    9  *
   10  * Unless required by applicable law or agreed to in writing, software
   11  * distributed under the License is distributed on an "AS IS" BASIS,
   12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13  * See the License for the specific language governing permissions and
   14  * limitations under the License.
   15  */
   16 
   17 import Foundation
   18 
   19 class SocketConnection: NSObject {
   20     var didOpen: (() -> Void)?
   21     var didClose: ((Error?) -> Void)?
   22     var streamHasSpaceAvailable: (() -> Void)?
   23 
   24     private let filePath: String
   25     private var socketHandle: Int32 = -1
   26     private var address: sockaddr_un?
   27 
   28     private var inputStream: InputStream?
   29     private var outputStream: OutputStream?
   30     
   31     private var networkQueue: DispatchQueue?
   32     private var shouldKeepRunning = false
   33 
   34     init?(filePath path: String) {
   35         filePath = path
   36         socketHandle = Darwin.socket(AF_UNIX, SOCK_STREAM, 0)
   37 
   38         guard socketHandle != -1 else {
   39             print("failure: create socket")
   40             return nil
   41         }
   42     }
   43 
   44     func open() -> Bool {
   45         print("open socket connection")
   46 
   47         guard FileManager.default.fileExists(atPath: filePath) else {
   48             print("failure: socket file missing")
   49             return false
   50         }
   51       
   52         guard setupAddress() == true else {
   53             return false
   54         }
   55         
   56         guard connectSocket() == true else {
   57             return false
   58         }
   59 
   60         setupStreams()
   61         
   62         inputStream?.open()
   63         outputStream?.open()
   64 
   65         return true
   66     }
   67 
   68     func close() {
   69         unscheduleStreams()
   70 
   71         inputStream?.delegate = nil
   72         outputStream?.delegate = nil
   73 
   74         inputStream?.close()
   75         outputStream?.close()
   76         
   77         inputStream = nil
   78         outputStream = nil
   79     }
   80 
   81     func writeToStream(buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int {
   82         return outputStream?.write(buffer, maxLength: length) ?? 0
   83     }
   84 }
   85 
   86 extension SocketConnection: StreamDelegate {
   87 
   88     func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
   89         switch eventCode {
   90         case .openCompleted:
   91             print("client stream open completed")
   92             if aStream == outputStream {
   93                 didOpen?()
   94             }
   95         case .hasBytesAvailable:
   96             if aStream == inputStream {
   97                 var buffer: UInt8 = 0
   98                 let numberOfBytesRead = inputStream?.read(&buffer, maxLength: 1)
   99                 if numberOfBytesRead == 0 && aStream.streamStatus == .atEnd {
  100                     print("server socket closed")
  101                     close()
  102                     notifyDidClose(error: nil)
  103                 }
  104             }
  105         case .hasSpaceAvailable:
  106             if aStream == outputStream {
  107                 streamHasSpaceAvailable?()
  108             }
  109         case .errorOccurred:
  110             print("client stream error occurred: \(String(describing: aStream.streamError))")
  111             close()
  112             notifyDidClose(error: aStream.streamError)
  113 
  114         default:
  115             break
  116         }
  117     }
  118 }
  119 
  120 private extension SocketConnection {
  121   
  122     func setupAddress() -> Bool {
  123         var addr = sockaddr_un()
  124         guard filePath.count < MemoryLayout.size(ofValue: addr.sun_path) else {
  125             print("failure: fd path is too long")
  126             return false
  127         }
  128 
  129         _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in
  130             filePath.withCString {
  131                 strncpy(ptr, $0, filePath.count)
  132             }
  133         }
  134         
  135         address = addr
  136         return true
  137     }
  138 
  139     func connectSocket() -> Bool {
  140         guard var addr = address else {
  141             return false
  142         }
  143         
  144         let status = withUnsafePointer(to: &addr) { ptr in
  145             ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) {
  146                 Darwin.connect(socketHandle, $0, socklen_t(MemoryLayout<sockaddr_un>.size))
  147             }
  148         }
  149 
  150         guard status == noErr else {
  151             print("failure: \(status)")
  152             return false
  153         }
  154         
  155         return true
  156     }
  157 
  158     func setupStreams() {
  159         var readStream: Unmanaged<CFReadStream>?
  160         var writeStream: Unmanaged<CFWriteStream>?
  161 
  162         CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketHandle, &readStream, &writeStream)
  163 
  164         inputStream = readStream?.takeRetainedValue()
  165         inputStream?.delegate = self
  166         inputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
  167 
  168         outputStream = writeStream?.takeRetainedValue()
  169         outputStream?.delegate = self
  170         outputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
  171 
  172         scheduleStreams()
  173     }
  174   
  175     func scheduleStreams() {
  176         shouldKeepRunning = true
  177         
  178         networkQueue = DispatchQueue.global(qos: .userInitiated)
  179         networkQueue?.async { [weak self] in
  180             self?.inputStream?.schedule(in: .current, forMode: .common)
  181             self?.outputStream?.schedule(in: .current, forMode: .common)
  182             
  183             var isRunning = false
  184                         
  185             repeat {
  186                 isRunning = self?.shouldKeepRunning ?? false && RunLoop.current.run(mode: .default, before: .distantFuture)
  187             } while (isRunning)
  188         }
  189     }
  190     
  191     func unscheduleStreams() {
  192         networkQueue?.sync { [weak self] in
  193             self?.inputStream?.remove(from: .current, forMode: .common)
  194             self?.outputStream?.remove(from: .current, forMode: .common)
  195         }
  196         
  197         shouldKeepRunning = false
  198     }
  199     
  200     func notifyDidClose(error: Error?) {
  201         if didClose != nil {
  202             didClose?(error)
  203         }
  204     }
  205 }