You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
6.8 KiB

5 years ago
  1. //
  2. // SocketParsable.swift
  3. // Socket.IO-Client-Swift
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. import Foundation
  23. /// Defines that a type will be able to parse socket.io-protocol messages.
  24. public protocol SocketParsable : AnyObject {
  25. // MARK: Methods
  26. /// Called when the engine has received some binary data that should be attached to a packet.
  27. ///
  28. /// Packets binary data should be sent directly after the packet that expects it, so there's confusion over
  29. /// where the data should go. Data should be received in the order it is sent, so that the correct data is put
  30. /// into the correct placeholder.
  31. ///
  32. /// - parameter data: The data that should be attached to a packet.
  33. func parseBinaryData(_ data: Data) -> SocketPacket?
  34. /// Called when the engine has received a string that should be parsed into a socket.io packet.
  35. ///
  36. /// - parameter message: The string that needs parsing.
  37. /// - returns: A completed socket packet if there is no more data left to collect.
  38. func parseSocketMessage(_ message: String) -> SocketPacket?
  39. }
  40. /// Errors that can be thrown during parsing.
  41. public enum SocketParsableError : Error {
  42. // MARK: Cases
  43. /// Thrown when a packet received has an invalid data array, or is missing the data array.
  44. case invalidDataArray
  45. /// Thrown when an malformed packet is received.
  46. case invalidPacket
  47. /// Thrown when the parser receives an unknown packet type.
  48. case invalidPacketType
  49. }
  50. /// Says that a type will be able to buffer binary data before all data for an event has come in.
  51. public protocol SocketDataBufferable : AnyObject {
  52. // MARK: Properties
  53. /// A list of packets that are waiting for binary data.
  54. ///
  55. /// The way that socket.io works all data should be sent directly after each packet.
  56. /// So this should ideally be an array of one packet waiting for data.
  57. ///
  58. /// **This should not be modified directly.**
  59. var waitingPackets: [SocketPacket] { get set }
  60. }
  61. public extension SocketParsable where Self: SocketManagerSpec & SocketDataBufferable {
  62. /// Parses a message from the engine, returning a complete SocketPacket or throwing.
  63. ///
  64. /// - parameter message: The message to parse.
  65. /// - returns: A completed packet, or throwing.
  66. internal func parseString(_ message: String) throws -> SocketPacket {
  67. var reader = SocketStringReader(message: message)
  68. guard let type = Int(reader.read(count: 1)).flatMap({ SocketPacket.PacketType(rawValue: $0) }) else {
  69. throw SocketParsableError.invalidPacketType
  70. }
  71. if !reader.hasNext {
  72. return SocketPacket(type: type, nsp: "/")
  73. }
  74. var namespace = "/"
  75. var placeholders = -1
  76. if type.isBinary {
  77. if let holders = Int(reader.readUntilOccurence(of: "-")) {
  78. placeholders = holders
  79. } else {
  80. throw SocketParsableError.invalidPacket
  81. }
  82. }
  83. if reader.currentCharacter == "/" {
  84. namespace = reader.readUntilOccurence(of: ",")
  85. }
  86. if !reader.hasNext {
  87. return SocketPacket(type: type, nsp: namespace, placeholders: placeholders)
  88. }
  89. var idString = ""
  90. if type == .error {
  91. reader.advance(by: -1)
  92. } else {
  93. while let int = Int(reader.read(count: 1)) {
  94. idString += String(int)
  95. }
  96. reader.advance(by: -2)
  97. }
  98. var dataArray = String(message.utf16[message.utf16.index(reader.currentIndex, offsetBy: 1)...])!
  99. if type == .error && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") {
  100. dataArray = "[" + dataArray + "]"
  101. }
  102. let data = try parseData(dataArray)
  103. return SocketPacket(type: type, data: data, id: Int(idString) ?? -1, nsp: namespace, placeholders: placeholders)
  104. }
  105. // Parses data for events
  106. private func parseData(_ data: String) throws -> [Any] {
  107. do {
  108. return try data.toArray()
  109. } catch {
  110. throw SocketParsableError.invalidDataArray
  111. }
  112. }
  113. /// Called when the engine has received a string that should be parsed into a socket.io packet.
  114. ///
  115. /// - parameter message: The string that needs parsing.
  116. /// - returns: A completed socket packet or nil if the packet is invalid.
  117. func parseSocketMessage(_ message: String) -> SocketPacket? {
  118. guard !message.isEmpty else { return nil }
  119. DefaultSocketLogger.Logger.log("Parsing \(message)", type: "SocketParser")
  120. do {
  121. let packet = try parseString(message)
  122. DefaultSocketLogger.Logger.log("Decoded packet as: \(packet.description)", type: "SocketParser")
  123. return packet
  124. } catch {
  125. DefaultSocketLogger.Logger.error("\(error): \(message)", type: "SocketParser")
  126. return nil
  127. }
  128. }
  129. /// Called when the engine has received some binary data that should be attached to a packet.
  130. ///
  131. /// Packets binary data should be sent directly after the packet that expects it, so there's confusion over
  132. /// where the data should go. Data should be received in the order it is sent, so that the correct data is put
  133. /// into the correct placeholder.
  134. ///
  135. /// - parameter data: The data that should be attached to a packet.
  136. /// - returns: A completed socket packet if there is no more data left to collect.
  137. func parseBinaryData(_ data: Data) -> SocketPacket? {
  138. guard !waitingPackets.isEmpty else {
  139. DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
  140. return nil
  141. }
  142. // Should execute event?
  143. guard waitingPackets[waitingPackets.count - 1].addData(data) else { return nil }
  144. return waitingPackets.removeLast()
  145. }
  146. }