WebConnectionContext.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2018-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import Foundation
  21. import CocoaLumberjackSwift
  22. protocol WebConnectionContextDelegate: class {
  23. func currentWCSession() -> WCSession
  24. }
  25. class WebConnectionContext: NSObject, NSCoding, NSCopying {
  26. var delegate: WebConnectionContextDelegate
  27. var chunkCache: WebChunkCache = WebChunkCache(sequenceNumber: WebSequenceNumber(minValue: 0, maxValue: UInt64(UINT32_MAX)))
  28. var connectionInfoRequest: WebUpdateConnectionInfoRequest?
  29. var unchunker: Unchunker = Unchunker()
  30. var messageCounter: UInt32 = 0
  31. var incomingSequenceNumber: UInt32 = 0
  32. var cacheTimer: Timer?
  33. var previousConnectionContext: WebConnectionContext? {
  34. get {
  35. return _previousContext
  36. }
  37. set(previous) {
  38. _previousContext = previous
  39. _previousContext?.previousConnectionContext = nil
  40. }
  41. }
  42. private var _connectionId: Data
  43. private var _previousContext: WebConnectionContext?
  44. init(connectionId:Data, delegate: WCConnection) {
  45. _connectionId = connectionId
  46. self.delegate = delegate
  47. }
  48. init(connectionId: Data, chunkCache: WebChunkCache, connectionInfoRequest: WebUpdateConnectionInfoRequest?, unchunker: Unchunker, messageCounter: UInt32, incomingSequenceNumber: UInt32, cacheTimer: Timer?, delegate: WCConnection) {
  49. self.chunkCache = chunkCache
  50. self.connectionInfoRequest = connectionInfoRequest
  51. self.unchunker = unchunker
  52. self.messageCounter = messageCounter
  53. self.incomingSequenceNumber = incomingSequenceNumber
  54. self.cacheTimer = cacheTimer
  55. self._connectionId = connectionId
  56. self.delegate = delegate
  57. }
  58. func connectionId() -> Data {
  59. return _connectionId
  60. }
  61. func runTimer() {
  62. cacheTimer?.invalidate()
  63. DispatchQueue.main.async {
  64. self.cacheTimer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.sendConnectionAck), userInfo: nil, repeats: true)
  65. }
  66. }
  67. func cancelTimer() {
  68. cacheTimer?.invalidate()
  69. cacheTimer = nil
  70. }
  71. @objc func sendConnectionAck() {
  72. let responseConnectionAck = WebConnectionAckUpdateResponse.init(requestId: nil, incomingSequenceNumber: incomingSequenceNumber)
  73. DDLogVerbose("Threema Web: MessagePack -> Send update/connectionAck")
  74. if delegate.currentWCSession().connectionStatus() == .ready {
  75. delegate.currentWCSession().sendMessageToWeb(blacklisted: true, msgpack: responseConnectionAck.messagePack())
  76. }
  77. }
  78. func transfer(fromCache: [[UInt8]?]) {
  79. chunkCache.transfer(fromCache: fromCache)
  80. }
  81. func append(chunk: [UInt8]?) {
  82. chunkCache.append(chunk: chunk)
  83. }
  84. func prune(theirSequenceNumber: UInt32) throws {
  85. try chunkCache.prune(theirSequenceNumber: theirSequenceNumber)
  86. }
  87. func updateUnchunker(oldUnchunker: Unchunker) {
  88. let unchunkerSerialize: [[UInt8]] = oldUnchunker.serialize()
  89. for chunk in unchunkerSerialize {
  90. let chunkData = Data.init(chunk)
  91. do {
  92. try unchunker.addChunk(bytes: chunkData)
  93. }
  94. catch {
  95. // error can't add old chunk
  96. }
  97. }
  98. }
  99. // MARK: NSCoding
  100. required init?(coder aDecoder: NSCoder) {
  101. // super.init(coder:) is optional, see notes below
  102. self.chunkCache = aDecoder.decodeObject(forKey: "chunkCache") as! WebChunkCache
  103. if let unchunkerSerialize = aDecoder.decodeObject(forKey: "unchunker") as? [[UInt8]] {
  104. for chunk in unchunkerSerialize {
  105. let chunkData = Data.init(chunk)
  106. do {
  107. try unchunker.addChunk(bytes: chunkData)
  108. }
  109. catch {
  110. // error can't add old chunk
  111. }
  112. }
  113. }
  114. self.messageCounter = UInt32(aDecoder.decodeInt32(forKey: "messageCounter"))
  115. self.incomingSequenceNumber = UInt32(aDecoder.decodeInt32(forKey: "incomingSequenceNumber"))
  116. self._connectionId = aDecoder.decodeObject(forKey: "_connectionId") as! Data
  117. if let previousContext = aDecoder.decodeObject(forKey: "previousConnectionContext") as? WebConnectionContext {
  118. self._previousContext = previousContext
  119. }
  120. self.delegate = aDecoder.decodeObject(forKey: "delegate") as! WebConnectionContextDelegate
  121. }
  122. func encode(with aCoder: NSCoder) {
  123. // super.encodeWithCoder(aCoder) is optional, see notes below
  124. aCoder.encode(chunkCache, forKey: "chunkCache")
  125. _ = unchunker.gc(maxAge: 20)
  126. aCoder.encode(unchunker.serialize(), forKey: "unchunker")
  127. aCoder.encode(Int32(messageCounter), forKey: "messageCounter")
  128. aCoder.encode(Int32(incomingSequenceNumber), forKey: "incomingSequenceNumber")
  129. aCoder.encode(_connectionId, forKey: "_connectionId")
  130. if _previousContext != nil {
  131. _previousContext!.previousConnectionContext = nil
  132. aCoder.encode(_previousContext, forKey: "previousConnectionContext")
  133. }
  134. aCoder.encode(delegate, forKey: "delegate")
  135. }
  136. func copy(with zone: NSZone? = nil) -> Any {
  137. let copy = WebConnectionContext.init(connectionId: _connectionId, chunkCache: chunkCache, connectionInfoRequest: connectionInfoRequest, unchunker: unchunker, messageCounter: messageCounter, incomingSequenceNumber: incomingSequenceNumber, cacheTimer: cacheTimer, delegate: delegate as! WCConnection)
  138. return copy
  139. }
  140. }