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.

107 lines
3.9 KiB

2 years ago
  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // FrameCollector.swift
  4. // Starscream
  5. //
  6. // Created by Dalton Cherry on 1/24/19.
  7. // Copyright © 2019 Vluxe. All rights reserved.
  8. //
  9. // Licensed under the Apache License, Version 2.0 (the "License");
  10. // you may not use this file except in compliance with the License.
  11. // You may obtain a copy of the License at
  12. //
  13. // http://www.apache.org/licenses/LICENSE-2.0
  14. //
  15. // Unless required by applicable law or agreed to in writing, software
  16. // distributed under the License is distributed on an "AS IS" BASIS,
  17. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. // See the License for the specific language governing permissions and
  19. // limitations under the License.
  20. //
  21. //////////////////////////////////////////////////////////////////////////////////////////////////
  22. import Foundation
  23. public protocol FrameCollectorDelegate: class {
  24. func didForm(event: FrameCollector.Event)
  25. func decompress(data: Data, isFinal: Bool) -> Data?
  26. }
  27. public class FrameCollector {
  28. public enum Event {
  29. case text(String)
  30. case binary(Data)
  31. case pong(Data?)
  32. case ping(Data?)
  33. case error(Error)
  34. case closed(String, UInt16)
  35. }
  36. weak var delegate: FrameCollectorDelegate?
  37. var buffer = Data()
  38. var frameCount = 0
  39. var isText = false //was the first frame a text frame or a binary frame?
  40. var needsDecompression = false
  41. public func add(frame: Frame) {
  42. //check single frame action and out of order frames
  43. if frame.opcode == .connectionClose {
  44. var code = frame.closeCode
  45. var reason = "connection closed by server"
  46. if let customCloseReason = String(data: frame.payload, encoding: .utf8) {
  47. reason = customCloseReason
  48. } else {
  49. code = CloseCode.protocolError.rawValue
  50. }
  51. delegate?.didForm(event: .closed(reason, code))
  52. return
  53. } else if frame.opcode == .pong {
  54. delegate?.didForm(event: .pong(frame.payload))
  55. return
  56. } else if frame.opcode == .ping {
  57. delegate?.didForm(event: .ping(frame.payload))
  58. return
  59. } else if frame.opcode == .continueFrame && frameCount == 0 {
  60. let errCode = CloseCode.protocolError.rawValue
  61. delegate?.didForm(event: .error(WSError(type: .protocolError, message: "first frame can't be a continue frame", code: errCode)))
  62. reset()
  63. return
  64. } else if frameCount > 0 && frame.opcode != .continueFrame {
  65. let errCode = CloseCode.protocolError.rawValue
  66. delegate?.didForm(event: .error(WSError(type: .protocolError, message: "second and beyond of fragment message must be a continue frame", code: errCode)))
  67. reset()
  68. return
  69. }
  70. if frameCount == 0 {
  71. isText = frame.opcode == .textFrame
  72. needsDecompression = frame.needsDecompression
  73. }
  74. let payload: Data
  75. if needsDecompression {
  76. payload = delegate?.decompress(data: frame.payload, isFinal: frame.isFin) ?? frame.payload
  77. } else {
  78. payload = frame.payload
  79. }
  80. buffer.append(payload)
  81. frameCount += 1
  82. if frame.isFin {
  83. if isText {
  84. if let string = String(data: buffer, encoding: .utf8) {
  85. delegate?.didForm(event: .text(string))
  86. } else {
  87. let errCode = CloseCode.protocolError.rawValue
  88. delegate?.didForm(event: .error(WSError(type: .protocolError, message: "not valid UTF-8 data", code: errCode)))
  89. }
  90. } else {
  91. delegate?.didForm(event: .binary(buffer))
  92. }
  93. reset()
  94. }
  95. }
  96. func reset() {
  97. buffer = Data()
  98. frameCount = 0
  99. }
  100. }