|
|
@@ -9,34 +9,42 @@ import Foundation
|
|
|
|
|
|
extension HttpHandlers {
|
|
|
|
|
|
- public class func websocket(handle:(String) -> ()) -> (HttpRequest -> HttpResponse) {
|
|
|
+ public class func websocket(text:(String -> Void)?, _ binary:([UInt8] -> Void)?) -> (HttpRequest -> HttpResponse) {
|
|
|
return { r in
|
|
|
guard r.headers["upgrade"] == "websocket" else {
|
|
|
- return .BadRequest
|
|
|
+ return .BadRequest(.Text("Invalid value of 'Upgrade' header: \(r.headers["upgrade"])"))
|
|
|
}
|
|
|
-
|
|
|
guard r.headers["connection"] == "Upgrade" else {
|
|
|
- return .BadRequest
|
|
|
+ return .BadRequest(.Text("Invalid value of 'Connection' header: \(r.headers["connection"])"))
|
|
|
}
|
|
|
guard let secWebSocketKey = r.headers["sec-websocket-key"] else {
|
|
|
- return .BadRequest
|
|
|
+ return .BadRequest(.Text("Invalid value of 'Sec-Websocket-Key' header: \(r.headers["sec-websocket-key"])"))
|
|
|
}
|
|
|
- let accept = String.encodeToBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").SHA1())
|
|
|
- let upgradeHeaders = [ "Upgrade": "WebSocket", "Connection": "Upgrade", "Sec-WebSocket-Accept": accept]
|
|
|
- return HttpResponse.SwitchProtocols(upgradeHeaders) { socket in
|
|
|
- let parser = WebSocketParser()
|
|
|
- while let frame = try? parser.readFrame(socket) {
|
|
|
- handle(String.fromUInt8(frame.payload))
|
|
|
+ let protocolSessionClosure: (Socket -> Void) = { socket in
|
|
|
+ let session = WebSocketSession(socket)
|
|
|
+ while let frame = try? session.readFrame(socket) {
|
|
|
+ switch frame.opcode {
|
|
|
+ case .TEXT:
|
|
|
+ if let handleText = text {
|
|
|
+ handleText(String.fromUInt8(frame.payload))
|
|
|
+ }
|
|
|
+ case .BINARY:
|
|
|
+ if let handleBinary = binary {
|
|
|
+ handleBinary(frame.payload)
|
|
|
+ }
|
|
|
+ default: break
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+ let secWebSocketAccept = String.encodeToBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").SHA1())
|
|
|
+ let headers = [ "Upgrade": "WebSocket", "Connection": "Upgrade", "Sec-WebSocket-Accept": secWebSocketAccept]
|
|
|
+ return HttpResponse.SwitchProtocols(headers, protocolSessionClosure)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public class WebSocketParser {
|
|
|
+ public class WebSocketSession {
|
|
|
|
|
|
- enum Error: ErrorType {
|
|
|
- case UnknownOpCode(String)
|
|
|
- }
|
|
|
+ public enum Error: ErrorType { case UnknownOpCode(String), UnMaskedFrame }
|
|
|
|
|
|
public enum OpCode { case CONTINUE, CLOSE, PING, PONG, TEXT, BINARY }
|
|
|
|
|
|
@@ -45,6 +53,20 @@ extension HttpHandlers {
|
|
|
public var fin = false
|
|
|
public var payload = [UInt8]()
|
|
|
}
|
|
|
+
|
|
|
+ private let socket: Socket
|
|
|
+
|
|
|
+ init(_ socket: Socket) {
|
|
|
+ self.socket = socket
|
|
|
+ }
|
|
|
+
|
|
|
+ public func writeText(text: String) -> Void {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public func writeBinary(binary: [UInt8]) -> Void {
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
public func readFrame(socket: Socket) throws -> Frame {
|
|
|
let frm = Frame()
|
|
|
@@ -58,12 +80,19 @@ extension HttpHandlers {
|
|
|
case 0x08: frm.opcode = OpCode.CLOSE
|
|
|
case 0x09: frm.opcode = OpCode.PING
|
|
|
case 0x0A: frm.opcode = OpCode.PONG
|
|
|
+ // "If an unknown opcode is received, the receiving endpoint MUST _Fail the WebSocket Connection_."
|
|
|
+ // http://tools.ietf.org/html/rfc6455#section-5.2 ( Page 29 )
|
|
|
default : throw Error.UnknownOpCode("\(opc)")
|
|
|
}
|
|
|
let sec = try socket.read()
|
|
|
let msk = sec & 0x0F != 0
|
|
|
+ guard msk else {
|
|
|
+ // "...a client MUST mask all frames that it sends to the serve.."
|
|
|
+ // http://tools.ietf.org/html/rfc6455#section-5.1
|
|
|
+ throw Error.UnMaskedFrame
|
|
|
+ }
|
|
|
var len = UInt64(sec & 0x7F)
|
|
|
- if len == 0x7E {1
|
|
|
+ if len == 0x7E {
|
|
|
let b0 = UInt64(try socket.read())
|
|
|
let b1 = UInt64(try socket.read())
|
|
|
len = UInt64(littleEndian: b0 << 8 | b1)
|
|
|
@@ -78,10 +107,9 @@ extension HttpHandlers {
|
|
|
let b7 = UInt64(try socket.read())
|
|
|
len = UInt64(littleEndian: b0 << 54 | b1 << 48 | b2 << 40 | b3 << 32 | b4 << 24 | b5 << 16 | b6 << 8 | b7)
|
|
|
}
|
|
|
- let mask = msk ? [try socket.read(), try socket.read(), try socket.read(), try socket.read()] : []
|
|
|
+ let mask = [try socket.read(), try socket.read(), try socket.read(), try socket.read()]
|
|
|
for i in 0..<len {
|
|
|
- let byte = try socket.read()
|
|
|
- frm.payload.append(msk ? byte ^ mask[Int(i % 4)] : byte)
|
|
|
+ frm.payload.append(try socket.read() ^ mask[Int(i % 4)])
|
|
|
}
|
|
|
return frm
|
|
|
}
|