WebSockets.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. //
  2. // HttpHandlers+WebSockets.swift
  3. // Swifter
  4. //
  5. // Copyright © 2014-2016 Damian Kołakowski. All rights reserved.
  6. //
  7. import Foundation
  8. public func websocket(
  9. _ text: ((WebSocketSession, String) -> Void)?,
  10. _ binary: ((WebSocketSession, [UInt8]) -> Void)?) -> ((HttpRequest) -> HttpResponse) {
  11. return { r in
  12. guard r.hasTokenForHeader("upgrade", token: "websocket") else {
  13. return .badRequest(.text("Invalid value of 'Upgrade' header: \(r.headers["upgrade"])"))
  14. }
  15. guard r.hasTokenForHeader("connection", token: "upgrade") else {
  16. return .badRequest(.text("Invalid value of 'Connection' header: \(r.headers["connection"])"))
  17. }
  18. guard let secWebSocketKey = r.headers["sec-websocket-key"] else {
  19. return .badRequest(.text("Invalid value of 'Sec-Websocket-Key' header: \(r.headers["sec-websocket-key"])"))
  20. }
  21. let protocolSessionClosure: ((Socket) -> Void) = { socket in
  22. let session = WebSocketSession(socket)
  23. var fragmentedOpCode = WebSocketSession.OpCode.close
  24. var payload = [UInt8]() // Used for fragmented frames.
  25. func handleTextPayload(_ frame: WebSocketSession.Frame) throws {
  26. if let handleText = text {
  27. if frame.fin {
  28. if payload.count > 0 {
  29. throw WebSocketSession.WsError.protocolError("Continuing fragmented frame cannot have an operation code.")
  30. }
  31. var textFramePayload = frame.payload.map { Int8(bitPattern: $0) }
  32. textFramePayload.append(0)
  33. if let text = String(validatingUTF8: textFramePayload) {
  34. handleText(session, text)
  35. } else {
  36. throw WebSocketSession.WsError.invalidUTF8("")
  37. }
  38. } else {
  39. payload.append(contentsOf: frame.payload)
  40. fragmentedOpCode = .text
  41. }
  42. }
  43. }
  44. func handleBinaryPayload(_ frame: WebSocketSession.Frame) throws {
  45. if let handleBinary = binary {
  46. if frame.fin {
  47. if payload.count > 0 {
  48. throw WebSocketSession.WsError.protocolError("Continuing fragmented frame cannot have an operation code.")
  49. }
  50. handleBinary(session, frame.payload)
  51. } else {
  52. payload.append(contentsOf: frame.payload)
  53. fragmentedOpCode = .binary
  54. }
  55. }
  56. }
  57. func handleOperationCode(_ frame: WebSocketSession.Frame) throws {
  58. switch frame.opcode {
  59. case .continue:
  60. // There is no message to continue, failed immediatelly.
  61. if fragmentedOpCode == .close {
  62. socket.shutdwn()
  63. }
  64. frame.opcode = fragmentedOpCode
  65. if frame.fin {
  66. payload.append(contentsOf: frame.payload)
  67. frame.payload = payload
  68. // Clean the buffer.
  69. payload = []
  70. // Reset the OpCode.
  71. fragmentedOpCode = WebSocketSession.OpCode.close
  72. }
  73. try handleOperationCode(frame)
  74. case .text:
  75. try handleTextPayload(frame)
  76. case .binary:
  77. try handleBinaryPayload(frame)
  78. case .close:
  79. throw WebSocketSession.Control.close
  80. case .ping:
  81. if frame.payload.count > 125 {
  82. throw WebSocketSession.WsError.protocolError("Payload gretter than 125 octets.")
  83. } else {
  84. session.writeFrame(ArraySlice(frame.payload), .pong)
  85. }
  86. case .pong:
  87. break
  88. }
  89. }
  90. do {
  91. while true {
  92. let frame = try session.readFrame()
  93. try handleOperationCode(frame)
  94. }
  95. } catch let error {
  96. switch error {
  97. case WebSocketSession.Control.close:
  98. // Normal close
  99. break
  100. case WebSocketSession.WsError.unknownOpCode:
  101. print("Unknown Op Code: \(error)")
  102. case WebSocketSession.WsError.unMaskedFrame:
  103. print("Unmasked frame: \(error)")
  104. case WebSocketSession.WsError.invalidUTF8:
  105. print("Invalid UTF8 character: \(error)")
  106. case WebSocketSession.WsError.protocolError:
  107. print("Protocol error: \(error)")
  108. default:
  109. print("Unkown error \(error)")
  110. }
  111. // If an error occurs, send the close handshake.
  112. session.writeCloseFrame()
  113. }
  114. }
  115. let secWebSocketAccept = String.toBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").sha1())
  116. let headers = [ "Upgrade": "WebSocket", "Connection": "Upgrade", "Sec-WebSocket-Accept": secWebSocketAccept]
  117. return HttpResponse.switchProtocols(headers, protocolSessionClosure)
  118. }
  119. }
  120. public class WebSocketSession: Hashable, Equatable {
  121. public enum WsError: Error { case unknownOpCode(String), unMaskedFrame(String), protocolError(String), invalidUTF8(String) }
  122. public enum OpCode: UInt8 { case `continue` = 0x00, close = 0x08, ping = 0x09, pong = 0x0A, text = 0x01, binary = 0x02 }
  123. public enum Control: Error { case close }
  124. public class Frame {
  125. public var opcode = OpCode.close
  126. public var fin = false
  127. public var rsv1: UInt8 = 0
  128. public var rsv2: UInt8 = 0
  129. public var rsv3: UInt8 = 0
  130. public var payload = [UInt8]()
  131. }
  132. let socket: Socket
  133. public init(_ socket: Socket) {
  134. self.socket = socket
  135. }
  136. deinit {
  137. writeCloseFrame()
  138. socket.shutdwn()
  139. }
  140. public func writeText(_ text: String) -> Void {
  141. self.writeFrame(ArraySlice(text.utf8), OpCode.text)
  142. }
  143. public func writeBinary(_ binary: [UInt8]) -> Void {
  144. self.writeBinary(ArraySlice(binary))
  145. }
  146. public func writeBinary(_ binary: ArraySlice<UInt8>) -> Void {
  147. self.writeFrame(binary, OpCode.binary)
  148. }
  149. public func writeFrame(_ data: ArraySlice<UInt8>, _ op: OpCode, _ fin: Bool = true) {
  150. let finAndOpCode = UInt8(fin ? 0x80 : 0x00) | op.rawValue
  151. let maskAndLngth = encodeLengthAndMaskFlag(UInt64(data.count), false)
  152. do {
  153. try self.socket.writeUInt8([finAndOpCode])
  154. try self.socket.writeUInt8(maskAndLngth)
  155. try self.socket.writeUInt8(data)
  156. } catch {
  157. print(error)
  158. }
  159. }
  160. public func writeCloseFrame() {
  161. writeFrame(ArraySlice("".utf8), .close)
  162. }
  163. private func encodeLengthAndMaskFlag(_ len: UInt64, _ masked: Bool) -> [UInt8] {
  164. let encodedLngth = UInt8(masked ? 0x80 : 0x00)
  165. var encodedBytes = [UInt8]()
  166. switch len {
  167. case 0...125:
  168. encodedBytes.append(encodedLngth | UInt8(len));
  169. case 126...UInt64(UINT16_MAX):
  170. encodedBytes.append(encodedLngth | 0x7E);
  171. encodedBytes.append(UInt8(len >> 8 & 0xFF));
  172. encodedBytes.append(UInt8(len >> 0 & 0xFF));
  173. default:
  174. encodedBytes.append(encodedLngth | 0x7F);
  175. encodedBytes.append(UInt8(len >> 56 & 0xFF));
  176. encodedBytes.append(UInt8(len >> 48 & 0xFF));
  177. encodedBytes.append(UInt8(len >> 40 & 0xFF));
  178. encodedBytes.append(UInt8(len >> 32 & 0xFF));
  179. encodedBytes.append(UInt8(len >> 24 & 0xFF));
  180. encodedBytes.append(UInt8(len >> 16 & 0xFF));
  181. encodedBytes.append(UInt8(len >> 08 & 0xFF));
  182. encodedBytes.append(UInt8(len >> 00 & 0xFF));
  183. }
  184. return encodedBytes
  185. }
  186. public func readFrame() throws -> Frame {
  187. let frm = Frame()
  188. let fst = try socket.read()
  189. frm.fin = fst & 0x80 != 0
  190. frm.rsv1 = fst & 0x40
  191. frm.rsv2 = fst & 0x20
  192. frm.rsv3 = fst & 0x10
  193. guard frm.rsv1 == 0 && frm.rsv2 == 0 && frm.rsv3 == 0
  194. else {
  195. throw WsError.protocolError("Reserved frame bit has not been negocitated.")
  196. }
  197. let opc = fst & 0x0F
  198. guard let opcode = OpCode(rawValue: opc) else {
  199. // "If an unknown opcode is received, the receiving endpoint MUST _Fail the WebSocket Connection_."
  200. // http://tools.ietf.org/html/rfc6455#section-5.2 ( Page 29 )
  201. throw WsError.unknownOpCode("\(opc)")
  202. }
  203. if frm.fin == false {
  204. switch opcode {
  205. case .ping, .pong, .close:
  206. // Control frames must not be fragmented
  207. // https://tools.ietf.org/html/rfc6455#section-5.5 ( Page 35 )
  208. throw WsError.protocolError("Control frames must not be fragmented.")
  209. default:
  210. break
  211. }
  212. }
  213. frm.opcode = opcode
  214. let sec = try socket.read()
  215. let msk = sec & 0x80 != 0
  216. guard msk else {
  217. // "...a client MUST mask all frames that it sends to the server."
  218. // http://tools.ietf.org/html/rfc6455#section-5.1
  219. throw WsError.unMaskedFrame("A client must mask all frames that it sends to the server.")
  220. }
  221. var len = UInt64(sec & 0x7F)
  222. if len == 0x7E {
  223. let b0 = UInt64(try socket.read())
  224. let b1 = UInt64(try socket.read())
  225. len = UInt64(littleEndian: b0 << 8 | b1)
  226. } else if len == 0x7F {
  227. let b0 = UInt64(try socket.read())
  228. let b1 = UInt64(try socket.read())
  229. let b2 = UInt64(try socket.read())
  230. let b3 = UInt64(try socket.read())
  231. let b4 = UInt64(try socket.read())
  232. let b5 = UInt64(try socket.read())
  233. let b6 = UInt64(try socket.read())
  234. let b7 = UInt64(try socket.read())
  235. len = UInt64(littleEndian: b0 << 54 | b1 << 48 | b2 << 40 | b3 << 32 | b4 << 24 | b5 << 16 | b6 << 8 | b7)
  236. }
  237. let mask = [try socket.read(), try socket.read(), try socket.read(), try socket.read()]
  238. for i in 0..<len {
  239. frm.payload.append(try socket.read() ^ mask[Int(i % 4)])
  240. }
  241. return frm
  242. }
  243. public var hashValue: Int {
  244. get {
  245. return socket.hashValue
  246. }
  247. }
  248. }
  249. public func ==(webSocketSession1: WebSocketSession, webSocketSession2: WebSocketSession) -> Bool {
  250. return webSocketSession1.socket == webSocketSession2.socket
  251. }