diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2020-08-05 19:18:16 +0300 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2020-08-18 17:18:20 +0200 |
| commit | 2e9143040fbd7f9e26b397ba6051e2f91b0f732d (patch) | |
| tree | b9d6e3cfdb4554ba20c473257648727fea4993b5 | |
| parent | 1ff1153eb12ac0607627d3d56ba4bfa69071c641 (diff) | |
| download | mullvadvpn-2e9143040fbd7f9e26b397ba6051e2f91b0f732d.tar.xz mullvadvpn-2e9143040fbd7f9e26b397ba6051e2f91b0f732d.zip | |
Add text file streamer
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/Logging/TextFileStream.swift | 117 |
2 files changed, 121 insertions, 0 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index de3226aa5d..6ce6bcc2c7 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 580EE22824B3289300F9D8A1 /* AssociatedValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE22724B3289300F9D8A1 /* AssociatedValue.swift */; }; 580EE22924B3289300F9D8A1 /* AssociatedValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE22724B3289300F9D8A1 /* AssociatedValue.swift */; }; 5811DE50239014550011EB53 /* NEVPNStatus+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5811DE4F239014550011EB53 /* NEVPNStatus+Debug.swift */; }; + 58141EC924DAC0ED0013F79C /* TextFileStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58141EC824DAC0ED0013F79C /* TextFileStream.swift */; }; 5815039724D6ECAE00C9C50E /* CustomFormatLogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5815039624D6ECAE00C9C50E /* CustomFormatLogHandler.swift */; }; 5815039824D6ECAE00C9C50E /* CustomFormatLogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5815039624D6ECAE00C9C50E /* CustomFormatLogHandler.swift */; }; 5815039D24D6ECE600C9C50E /* TextFileOutputStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5815039C24D6ECE600C9C50E /* TextFileOutputStream.swift */; }; @@ -268,6 +269,7 @@ 580EE22324B3243100F9D8A1 /* AsyncBlockOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncBlockOperation.swift; sourceTree = "<group>"; }; 580EE22724B3289300F9D8A1 /* AssociatedValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssociatedValue.swift; sourceTree = "<group>"; }; 5811DE4F239014550011EB53 /* NEVPNStatus+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEVPNStatus+Debug.swift"; sourceTree = "<group>"; }; + 58141EC824DAC0ED0013F79C /* TextFileStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFileStream.swift; sourceTree = "<group>"; }; 5815039324D6EB7200C9C50E /* LogRotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogRotation.swift; sourceTree = "<group>"; }; 5815039624D6ECAE00C9C50E /* CustomFormatLogHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFormatLogHandler.swift; sourceTree = "<group>"; }; 5815039C24D6ECE600C9C50E /* TextFileOutputStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFileOutputStream.swift; sourceTree = "<group>"; }; @@ -465,6 +467,7 @@ 5815039C24D6ECE600C9C50E /* TextFileOutputStream.swift */, 581503A524D6F4AE00C9C50E /* Logging.swift */, 5815039324D6EB7200C9C50E /* LogRotation.swift */, + 58141EC824DAC0ED0013F79C /* TextFileStream.swift */, ); path = Logging; sourceTree = "<group>"; @@ -1032,6 +1035,7 @@ 58561C99239A5D1500BD6B5E /* IPEndpoint.swift in Sources */, 58FD5BF22424F7D700112C88 /* UserInterfaceInteractionRestriction.swift in Sources */, 5811DE50239014550011EB53 /* NEVPNStatus+Debug.swift in Sources */, + 58141EC924DAC0ED0013F79C /* TextFileStream.swift in Sources */, 58C3A4B222456F1B00340BDB /* AccountInputGroupView.swift in Sources */, 58F840B22464491D0044E708 /* ChainedError.swift in Sources */, 58FAEDFF24533A7000CB0F5B /* KeychainReturn.swift in Sources */, diff --git a/ios/MullvadVPN/Logging/TextFileStream.swift b/ios/MullvadVPN/Logging/TextFileStream.swift new file mode 100644 index 0000000000..2ae9b3e82f --- /dev/null +++ b/ios/MullvadVPN/Logging/TextFileStream.swift @@ -0,0 +1,117 @@ +// +// TextFileStream.swift +// MullvadVPN +// +// Created by pronebird on 05/08/2020. +// Copyright © 2020 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import Darwin + +class TextFileStream<Codec> where Codec: UnicodeCodec { + private let readSource: DispatchSourceRead + private let fileDescriptor: Int32 + private let queue = DispatchQueue(label: "net.mullvad.MullvadVPN.TextFileStream<\(Codec.self)>") + private let stringStream: StringStreamIterator<Codec> + + init?(fileURL: URL, separator: Character, encoding: String.Encoding = .utf8) { + let filePath = fileURL.path.utf8CString.map { $0 } + + let fileDescriptor = open(filePath, O_RDONLY) + if (fileDescriptor == -1) { + return nil + } + + // Avoid blocking the read operation + _ = fcntl(fileDescriptor, F_SETFL, O_NONBLOCK); + + let readSource = DispatchSource.makeReadSource(fileDescriptor: fileDescriptor, queue: queue) + readSource.setCancelHandler { + close(fileDescriptor) + } + + stringStream = StringStreamIterator(separator: separator) + + self.readSource = readSource + self.fileDescriptor = fileDescriptor + } + + deinit { + readSource.cancel() + } + + func read(_ handler: @escaping (String) -> Void) { + readSource.setEventHandler { [weak self] in + guard let self = self else { return } + + let estimated = Int(self.readSource.data + 1) + var buffer = [Codec.CodeUnit](repeating: 0, count: estimated) + let actual = Darwin.read(self.fileDescriptor, &buffer, estimated) + + if actual == -1 { + print("TextFileInputStream: read error: \(errno)") + } + + if actual > 0 { + let bytes = buffer[..<actual] + self.stringStream.append(bytes: bytes) + + while let s = self.stringStream.next() { + handler(s) + } + } + } + readSource.activate() + } + + func cancel() { + readSource.cancel() + } + +} + +class StringStreamIterator<Codec>: IteratorProtocol where Codec: UnicodeCodec { + let separator: Character + + private var string = "" + private var data = [Codec.CodeUnit]() + private var parser = Codec.ForwardParser() + + init(separator: Character) { + self.separator = separator + } + + func append<S>(bytes: S) where S: Sequence, S.Element == Codec.CodeUnit { + data.append(contentsOf: bytes) + } + + func next() -> String? { + var dataIterator = data.makeIterator() + var bytesRead = 0 + + defer { + if bytesRead > 0 { + data.removeSubrange(..<bytesRead) + } + } + + while case .valid(let encodedScalar) = parser.parseScalar(from: &dataIterator) { + let unicodeScalar = Codec.decode(encodedScalar) + let character = Character(unicodeScalar) + + bytesRead += encodedScalar.count + + if character == separator { + let returnString = string + string = "" + + return returnString + } else { + string.append(character) + } + } + + return nil + } +} |
