Bläddra i källkod

Removed HttpHandlers namespace.

Damian Kołakowski 10 år sedan
förälder
incheckning
e5e4bdfecc

+ 103 - 75
Sources/Swifter/DemoServer.swift

@@ -5,7 +5,11 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-import Foundation
+#if os(Linux)
+    import Glibc
+#else
+    import Foundation
+#endif
 
 public func demoServer(_ publicDir: String) -> HttpServer {
     
@@ -13,9 +17,12 @@ public func demoServer(_ publicDir: String) -> HttpServer {
     
     let server = HttpServer()
     
-    server["/public/:path"] = HttpHandlers.shareFilesFromDirectory(publicDir)
-
-    server["/"] = HttpHandlers.scopes {
+    server["/public/:path"] = shareFilesFromDirectory(publicDir)
+    server["/public/"] = shareFilesFromDirectory(publicDir)    // needed to serve index file at root level
+    
+    server["/files/:path"] = directoryBrowser("/")
+    
+    server["/"] = scopes {
         html {
             body {
                 ul(server.routes) { service in
@@ -30,28 +37,63 @@ public func demoServer(_ publicDir: String) -> HttpServer {
     server["/magic"] = { .OK(.Html("You asked for " + $0.path)) }
     
     server["/test/:param1/:param2"] = { r in
-        var headersInfo = ""
-        for (name, value) in r.headers {
-            headersInfo += "\(name) : \(value)<br>"
-        }
-        var queryParamsInfo = ""
-        for (name, value) in r.queryParams {
-            queryParamsInfo += "\(name) : \(value)<br>"
-        }
-        var pathParamsInfo = ""
-        for token in r.params {
-            pathParamsInfo += "\(token.0) : \(token.1)<br>"
-        }
-        return .OK(.Html("<h3>Address: \(r.address)</h3><h3>Url:</h3> \(r.path)<h3>Method:</h3>\(r.method)<h3>Headers:</h3>\(headersInfo)<h3>Query:</h3>\(queryParamsInfo)<h3>Path params:</h3>\(pathParamsInfo)"))
+        scopes {
+            html {
+                body {
+                    h3 { inner = "Address: \(r.address)" }
+                    h3 { inner = "Url: \(r.path)" }
+                    h3 { inner = "Method: \(r.method)" }
+                    
+                    h3 { inner = "Query:" }
+                    
+                    table(r.queryParams) { param in
+                        tr {
+                            td { inner = param.0 }
+                            td { inner = param.1 }
+                        }
+                    }
+                    
+                    h3 { inner = "Headers:" }
+                    
+                    table(r.headers) { header in
+                        tr {
+                            td { inner = header.0 }
+                            td { inner = header.1 }
+                        }
+                    }
+                    
+                    h3 { inner = "Route params:" }
+                    
+                    table(r.params) { param in
+                        tr {
+                            td { inner = param.0 }
+                            td { inner = param.1 }
+                        }
+                    }
+                }
+            }
+            }(r)
     }
     
-    server.GET["/upload"] = { r in
-        if let html = NSData(contentsOfFile:"\(publicDir)/file.html") {
-            var array = [UInt8](repeating: 0, count: html.length)
-            html.getBytes(&array, length: html.length)
-            return HttpResponse.RAW(200, "OK", nil, { $0.write(array) })
+    server.GET["/upload"] = scopes {
+        html {
+            body {
+                form {
+                    method = "POST"
+                    action = "/upload"
+                    enctype = "multipart/form-data"
+                    
+                    input { name = "my_file1"; type = "file" }
+                    input { name = "my_file2"; type = "file" }
+                    input { name = "my_file3"; type = "file" }
+                    
+                    button {
+                        type = "submit"
+                        inner = "Upload"
+                    }
+                }
+            }
         }
-        return .NotFound
     }
     
     server.POST["/upload"] = { r in
@@ -62,13 +104,37 @@ public func demoServer(_ publicDir: String) -> HttpServer {
         return HttpResponse.OK(.Html(response))
     }
     
-    server.GET["/login"] = { r in
-        if let html = NSData(contentsOfFile:"\(publicDir)/login.html") {
-            var array = [UInt8](repeating: 0, count: html.length)
-            html.getBytes(&array, length: html.length)
-            return HttpResponse.RAW(200, "OK", nil, { $0.write(array) })
+    server.GET["/login"] = scopes {
+        html {
+            head {
+                script { src = "http://cdn.staticfile.org/jquery/2.1.4/jquery.min.js" }
+                stylesheet { href = "http://cdn.staticfile.org/twitter-bootstrap/3.3.0/css/bootstrap.min.css" }
+            }
+            body {
+                h3 { inner = "Sign In" }
+                
+                form {
+                    method = "POST"
+                    action = "/login"
+                    
+                    fieldset {
+                        input { placeholder = "E-mail"; name = "email"; type = "email"; autofocus = "" }
+                        input { placeholder = "Password"; name = "password"; type = "password"; autofocus = "" }
+                        a {
+                            href = "/login"
+                            button {
+                                type = "submit"
+                                inner = "Login"
+                            }
+                        }
+                    }
+                    
+                }
+                javascript {
+                    src = "http://cdn.staticfile.org/twitter-bootstrap/3.3.0/js/bootstrap.min.js"
+                }
+            }
         }
-        return .NotFound
     }
     
     server.POST["/login"] = { r in
@@ -76,7 +142,7 @@ public func demoServer(_ publicDir: String) -> HttpServer {
         return HttpResponse.OK(.Html(formFields.map({ "\($0.0) = \($0.1)" }).joined(separator: "<br>")))
     }
     
-    server["/demo"] = HttpHandlers.scopes {
+    server["/demo"] = scopes {
         html {
             body {
                 center {
@@ -94,7 +160,7 @@ public func demoServer(_ publicDir: String) -> HttpServer {
     server["/redirect"] = { r in
         return .MovedPermanently("http://www.google.com")
     }
-
+    
     server["/long"] = { r in
         var longResponse = ""
         for k in 0..<1000 { longResponse += "(\(k)),->" }
@@ -108,62 +174,24 @@ public func demoServer(_ publicDir: String) -> HttpServer {
     server["/stream"] = { r in
         return HttpResponse.RAW(200, "OK", nil, { w in
             for i in 0...100 {
-                w.write([UInt8]("[chunk \(i)]".utf8));
+                w.write([UInt8]("[chunk \(i)]".utf8))
             }
         })
     }
     
-    server["/websocket-echo"] = HttpHandlers.websocket({ (session, text) in
+    server["/websocket-echo"] = websocket({ (session, text) in
         session.writeText(text)
-    }, { (session, binary) in
-        session.writeBinary(binary)
+        }, { (session, binary) in
+            session.writeBinary(binary)
     })
     
-    server.get("/get-via-closure") { r in
-        return .OK(.Html("GET OK"))
-    }
-    
     server.notFoundHandler = { r in
         return .MovedPermanently("https://github.com/404")
     }
     
-    server.middleware.append({ r in
-        print("\(r.method) - \(r.path)")
+    server.middleware.append { r in
+        print("Middleware:\(r.method) \(r.path)")
         return nil
-    })
-    
-    server.GET["/scopes-demo"] = HttpHandlers.scopes {
-        html {
-            lang = "en"
-            head {
-                meta {
-                    name = "Scopes DSL"
-                    content = "Swift"
-                }
-                title {
-                    inner = "Demo Web Page for Scopes DSL"
-                }
-                stylesheet {
-                    href = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
-                }
-            }
-            body {
-                table {
-                    thead {
-                        tr {
-                            th { inner = "Number" }
-                            th { inner = "Square" }
-                        }
-                    }
-                    tbody(0..<1000) { i in
-                        tr {
-                            td { inner = "\(i)" }
-                            td { inner = "\(i*i)" }
-                        }
-                    }
-                }
-            }
-        }
     }
     
     return server

+ 42 - 0
Sources/Swifter/File.swift

@@ -18,6 +18,7 @@ public enum FileError: ErrorProtocol {
     case SeekFailed(String)
     case GetCurrentWorkingDirectoryFailed(String)
     case IsDirectoryFailed(String)
+    case OpenDirFailed(String)
 }
 
 public class File {
@@ -56,6 +57,47 @@ public class File {
         return String(cString: path)
     }
     
+    public static func isDirectory(path: String) throws -> Bool {
+        var s = stat()
+        guard path.withCString({ stat($0, &s) }) == 0 else {
+            throw FileError.IsDirectoryFailed(descriptionOfLastError())
+        }
+        return s.st_mode & S_IFMT == S_IFDIR
+    }
+    
+    public static func exists(_ path: String) throws -> Bool {
+        var buffer = stat()
+        return path.withCString({ stat($0, &buffer) == 0 })
+    }
+    
+    public static func list(_ path: String) throws -> [String] {
+        let dir = path.withCString { opendir($0) }
+        if dir == nil {
+            throw FileError.OpenDirFailed(descriptionOfLastError())
+        }
+        defer { closedir(dir) }
+        var results = [String]()
+        while true {
+            guard let ent = readdir(dir) else {
+                break
+            }
+            var name = ent.pointee.d_name
+            let fileName = withUnsafePointer(&name) { (ptr) -> String? in
+                #if os(Linux)
+                    return String.fromCString([CChar](UnsafeBufferPointer<CChar>(start: UnsafePointer(unsafeBitCast(ptr, UnsafePointer<CChar>.self)), count: 256)))
+                #else
+                    var buffer = [CChar](UnsafeBufferPointer(start: unsafeBitCast(ptr, to: UnsafePointer<CChar>.self), count: Int(ent.pointee.d_namlen)))
+                    buffer.append(0)
+                    return String(validatingUTF8: buffer)
+                #endif
+            }
+            if let fileName = fileName {
+                results.append(fileName)
+            }
+        }
+        return results
+    }
+    
     internal let pointer: UnsafeMutablePointer<FILE>
     
     public init(_ pointer: UnsafeMutablePointer<FILE>) {

+ 68 - 0
Sources/Swifter/Files.swift

@@ -0,0 +1,68 @@
+//
+//  HttpHandlers+Files.swift
+//  Swifter
+//
+//  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
+//
+
+import Foundation
+
+public func shareFilesFromDirectory(_ directoryPath: String) -> ((HttpRequest) -> HttpResponse) {
+    return { r in
+        guard let fileRelativePath = r.params.first else {
+            return .NotFound
+        }
+        let absolutePath = directoryPath + "/" + fileRelativePath.1
+        guard let file = try? File.openForReading(absolutePath) else {
+            return .NotFound
+        }
+        return .RAW(200, "OK", [:], { writer in
+            writer.write(file)
+            file.close()
+        })
+    }
+}
+
+public func directoryBrowser(_ dir: String) -> ((HttpRequest) -> HttpResponse) {
+    return { r in
+        guard let (_, value) = r.params.first else {
+            return HttpResponse.NotFound
+        }
+        let filePath = dir + "/" + value
+        do {
+            guard try File.exists(filePath) else {
+                return HttpResponse.NotFound
+            }
+            if try File.isDirectory(filePath) {
+                let files = try File.list(filePath)
+                return scopes {
+                    html {
+                        body {
+                            table(files) { file in
+                                tr {
+                                    td {
+                                        a {
+                                            href = r.path + "/" + file
+                                            inner = file
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    }(r)
+            } else {
+                guard let file = try? File.openForReading(filePath) else {
+                    return .NotFound
+                }
+                return .RAW(200, "OK", [:], { writer in
+                    writer.write(file)
+                    file.close()
+                })
+            }
+        } catch {
+            return HttpResponse.InternalServerError
+        }
+    }
+}
+

+ 0 - 27
Sources/Swifter/HttpHandlers+Files.swift

@@ -1,27 +0,0 @@
-//
-//  HttpHandlers+Files.swift
-//  Swifter
-//
-//  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
-//
-
-import Foundation
-
-extension HttpHandlers {
-    
-    public class func shareFilesFromDirectory(_ directoryPath: String) -> ((HttpRequest) -> HttpResponse) {
-        return { r in
-            guard let fileRelativePath = r.params.first else {
-                return .NotFound
-            }
-            let absolutePath = directoryPath + "/" + fileRelativePath.1
-            guard let file = try? File.openForReading(absolutePath) else {
-                return .NotFound
-            }
-            return .RAW(200, "OK", [:], { writer in
-                writer.write(file)
-                file.close()
-            })
-        }
-    }
-}

+ 0 - 153
Sources/Swifter/HttpHandlers+WebSockets.swift

@@ -1,153 +0,0 @@
-//
-//  HttpHandlers+WebSockets.swift
-//  Swifter
-//
-//  Copyright © 2014-2016 Damian Kołakowski. All rights reserved.
-//
-
-import Foundation
-
-extension HttpHandlers {
-    
-    public class func websocket(
-          _ text: ((WebSocketSession, String) -> Void)?,
-        _ binary: ((WebSocketSession, [UInt8]) -> Void)?) -> ((HttpRequest) -> HttpResponse) {
-        return { r in
-            guard r.hasTokenForHeader("upgrade", token: "websocket") else {
-                return .BadRequest(.Text("Invalid value of 'Upgrade' header: \(r.headers["upgrade"])"))
-            }
-            guard r.hasTokenForHeader("connection", token: "upgrade") else {
-                return .BadRequest(.Text("Invalid value of 'Connection' header: \(r.headers["connection"])"))
-            }
-            guard let secWebSocketKey = r.headers["sec-websocket-key"] else {
-                return .BadRequest(.Text("Invalid value of 'Sec-Websocket-Key' header: \(r.headers["sec-websocket-key"])"))
-            }
-            let protocolSessionClosure: ((Socket) -> Void) = { socket in
-                let session = WebSocketSession(socket)
-                while let frame = try? session.readFrame() {
-                    switch frame.opcode {
-                    case .Text:
-                        if let handleText = text {
-                            handleText(session, String.fromUInt8(frame.payload))
-                        }
-                    case .Binary:
-                        if let handleBinary = binary {
-                            handleBinary(session, frame.payload)
-                        }
-                    default: break
-                    }
-                }
-            }
-            let secWebSocketAccept = String.toBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").SHA1())
-            let headers = [ "Upgrade": "WebSocket", "Connection": "Upgrade", "Sec-WebSocket-Accept": secWebSocketAccept]
-            return HttpResponse.SwitchProtocols(headers, protocolSessionClosure)
-        }
-    }
-    
-    public class WebSocketSession {
-        
-        public enum Error: ErrorProtocol { case UnknownOpCode(String), UnMaskedFrame }
-        public enum OpCode: UInt8 { case Continue = 0x00, Close = 0x08, Ping = 0x09, Pong = 0x0A, Text = 0x01, Binary = 0x02 }
-        
-        public class Frame {
-            public var opcode = OpCode.Close
-            public var fin = false
-            public var payload = [UInt8]()
-        }
-
-        private let socket: Socket
-        
-        public init(_ socket: Socket) {
-            self.socket = socket
-        }
-        
-        public func writeText(_ text: String) -> Void {
-            self.writeFrame(ArraySlice(text.utf8), OpCode.Text)
-        }
-    
-        public func writeBinary(_ binary: [UInt8]) -> Void {
-            self.writeBinary(ArraySlice(binary))
-        }
-        
-        public func writeBinary(_ binary: ArraySlice<UInt8>) -> Void {
-            self.writeFrame(binary, OpCode.Binary)
-        }
-        
-        private func writeFrame(_ data: ArraySlice<UInt8>, _ op: OpCode, _ fin: Bool = true) {
-            let finAndOpCode = UInt8(fin ? 0x80 : 0x00) | op.rawValue
-            let maskAndLngth = encodeLengthAndMaskFlag(UInt64(data.count), false)
-            do {
-                try self.socket.writeUInt8([finAndOpCode])
-                try self.socket.writeUInt8(maskAndLngth)
-                try self.socket.writeUInt8(data)
-            } catch {
-                print(error)
-            }
-        }
-        
-        private func encodeLengthAndMaskFlag(_ len: UInt64, _ masked: Bool) -> [UInt8] {
-            let encodedLngth = UInt8(masked ? 0x80 : 0x00)
-            var encodedBytes = [UInt8]()
-            switch len {
-            case 0...125:
-                encodedBytes.append(encodedLngth | UInt8(len));
-            case 126...UInt64(UINT16_MAX):
-                encodedBytes.append(encodedLngth | 0x7E);
-                encodedBytes.append(UInt8(len >> 8));
-                encodedBytes.append(UInt8(len & 0xFF));
-            default:
-                encodedBytes.append(encodedLngth | 0x7F);
-                encodedBytes.append(UInt8(len >> 56) & 0xFF);
-                encodedBytes.append(UInt8(len >> 48) & 0xFF);
-                encodedBytes.append(UInt8(len >> 40) & 0xFF);
-                encodedBytes.append(UInt8(len >> 32) & 0xFF);
-                encodedBytes.append(UInt8(len >> 24) & 0xFF);
-                encodedBytes.append(UInt8(len >> 16) & 0xFF);
-                encodedBytes.append(UInt8(len >> 08) & 0xFF);
-                encodedBytes.append(UInt8(len >> 00) & 0xFF);
-            }
-            return encodedBytes
-        }
-        
-        public func readFrame() throws -> Frame {
-            let frm = Frame()
-            let fst = try socket.read()
-            frm.fin = fst & 0x80 != 0
-            let opc = fst & 0x0F
-            guard let opcode = OpCode(rawValue: opc) else {
-                // "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 )
-                throw Error.UnknownOpCode("\(opc)")
-            }
-            frm.opcode = opcode
-            let sec = try socket.read()
-            let msk = sec & 0x80 != 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 {
-                let b0 = UInt64(try socket.read())
-                let b1 = UInt64(try socket.read())
-                len = UInt64(littleEndian: b0 << 8 | b1)
-            } else if len == 0x7F {
-                let b0 = UInt64(try socket.read())
-                let b1 = UInt64(try socket.read())
-                let b2 = UInt64(try socket.read())
-                let b3 = UInt64(try socket.read())
-                let b4 = UInt64(try socket.read())
-                let b5 = UInt64(try socket.read())
-                let b6 = UInt64(try socket.read())
-                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 = [try socket.read(), try socket.read(), try socket.read(), try socket.read()]
-            for i in 0..<len {
-                frm.payload.append(try socket.read() ^ mask[Int(i % 4)])
-            }
-            return frm
-        }
-    }
-}

+ 0 - 12
Sources/Swifter/HttpHandlers.swift

@@ -1,12 +0,0 @@
-//
-//  Handlers.swift
-//  Swifter
-//
-//  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
-//
-
-import Foundation
-
-public class HttpHandlers {
-
-}

+ 22 - 15
Sources/Swifter/HttpHandlers+Scopes.swift → Sources/Swifter/Scopes.swift

@@ -5,17 +5,18 @@
 //  Copyright © 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-import Foundation
+#if os(Linux)
+    import Glibc
+#else
+    import Foundation
+#endif
 
-extension HttpHandlers {
-    
-    public class func scopes(_ c: Closure) -> ((HttpRequest) -> HttpResponse) {
-        return { r in
-            ScopesBuffer[Process.TID] = ""
-            c()
-            return .RAW(200, "OK", ["Content-Type": "text/html"],
-                        { $0.write([UInt8](("<!DOCTYPE html>"  + (ScopesBuffer[Process.TID] ?? "")).utf8)) })
-        }
+public func scopes(scope: Closure) -> ((HttpRequest) -> HttpResponse) {
+    return { r in
+        ScopesBuffer[Process.TID] = ""
+        scope()
+        return .RAW(200, "OK", ["Content-Type": "text/html"],
+                    { $0.write([UInt8](("<!DOCTYPE html>"  + (ScopesBuffer[Process.TID] ?? "")).utf8)) })
     }
 }
 
@@ -127,6 +128,7 @@ public var maxlength: String? = nil
 public var valuetype: String? = nil
 public var accesskey: String? = nil
 public var onmouseup: String? = nil
+public var autofocus: String? = nil
 public var onkeypress: String? = nil
 public var ondblclick: String? = nil
 public var onmouseout: String? = nil
@@ -139,6 +141,7 @@ public var onmousedown: String? = nil
 public var frameborder: String? = nil
 public var marginwidth: String? = nil
 public var cellspacing: String? = nil
+public var placeholder: String? = nil
 public var marginheight: String? = nil
 public var acceptCharset: String? = nil
 
@@ -320,7 +323,7 @@ public func template(c: Closure) { element("template", c) }
 public func textarea(c: Closure) { element("textarea", c) }
 
 public func plaintext(c: Closure) { element("plaintext", c) }
-
+public func javascript(c: Closure) { element("script", ["type": "text/javascript"], c) }
 public func blockquote(c: Closure) { element("blockquote", c) }
 public func figcaption(c: Closure) { element("figcaption", c) }
 
@@ -453,6 +456,7 @@ private func evaluate(_ node: String, _ attrs: [String: String?] = [:], _ c: Clo
     let stackframeborder = frameborder
     let stackmarginwidth = marginwidth
     let stackcellspacing = cellspacing
+    let stackplaceholder = placeholder
     let stackmarginheight = marginheight
     let stackacceptCharset = acceptCharset
     let stackinner = inner
@@ -575,6 +579,7 @@ private func evaluate(_ node: String, _ attrs: [String: String?] = [:], _ c: Clo
     cellpadding = nil
     onmousedown = nil
     frameborder = nil
+    placeholder = nil
     marginwidth = nil
     cellspacing = nil
     marginheight = nil
@@ -717,16 +722,17 @@ private func evaluate(_ node: String, _ attrs: [String: String?] = [:], _ c: Clo
     if let frameborder = frameborder { mergedAttributes["frameborder"] = frameborder }
     if let marginwidth = marginwidth { mergedAttributes["marginwidth"] = marginwidth }
     if let cellspacing = cellspacing { mergedAttributes["cellspacing"] = cellspacing }
+    if let placeholder = placeholder { mergedAttributes["placeholder"] = placeholder }
     if let marginheight = marginheight { mergedAttributes["marginheight"] = marginheight }
     if let acceptCharset = acceptCharset { mergedAttributes["accept-charset"] = acceptCharset }
-
+    
     for item in attrs.enumerated() {
-        mergedAttributes.updateValue(item.element.value, forKey: item.element.key)
+        mergedAttributes.updateValue(item.element.1, forKey: item.element.0)
     }
     
     output = output + mergedAttributes.reduce("") {
-        if let value = $0.1.value {
-            return $0.0 + " \($0.1.key)=\"\(value)\""
+        if let value = $0.1.1 {
+            return $0.0 + " \($0.1.0)=\"\(value)\""
         } else {
             return $0.0
         }
@@ -857,6 +863,7 @@ private func evaluate(_ node: String, _ attrs: [String: String?] = [:], _ c: Clo
     cellpadding = stackcellpadding
     onmousedown = stackonmousedown
     frameborder = stackframeborder
+    placeholder = stackplaceholder
     marginwidth = stackmarginwidth
     cellspacing = stackcellspacing
     marginheight = stackmarginheight

+ 151 - 0
Sources/Swifter/WebSockets.swift

@@ -0,0 +1,151 @@
+//
+//  HttpHandlers+WebSockets.swift
+//  Swifter
+//
+//  Copyright © 2014-2016 Damian Kołakowski. All rights reserved.
+//
+
+import Foundation
+
+
+public func websocket(
+      _ text: ((WebSocketSession, String) -> Void)?,
+    _ binary: ((WebSocketSession, [UInt8]) -> Void)?) -> ((HttpRequest) -> HttpResponse) {
+    return { r in
+        guard r.hasTokenForHeader("upgrade", token: "websocket") else {
+            return .BadRequest(.Text("Invalid value of 'Upgrade' header: \(r.headers["upgrade"])"))
+        }
+        guard r.hasTokenForHeader("connection", token: "upgrade") else {
+            return .BadRequest(.Text("Invalid value of 'Connection' header: \(r.headers["connection"])"))
+        }
+        guard let secWebSocketKey = r.headers["sec-websocket-key"] else {
+            return .BadRequest(.Text("Invalid value of 'Sec-Websocket-Key' header: \(r.headers["sec-websocket-key"])"))
+        }
+        let protocolSessionClosure: ((Socket) -> Void) = { socket in
+            let session = WebSocketSession(socket)
+            while let frame = try? session.readFrame() {
+                switch frame.opcode {
+                case .Text:
+                    if let handleText = text {
+                        handleText(session, String.fromUInt8(frame.payload))
+                    }
+                case .Binary:
+                    if let handleBinary = binary {
+                        handleBinary(session, frame.payload)
+                    }
+                default: break
+                }
+            }
+        }
+        let secWebSocketAccept = String.toBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").SHA1())
+        let headers = [ "Upgrade": "WebSocket", "Connection": "Upgrade", "Sec-WebSocket-Accept": secWebSocketAccept]
+        return HttpResponse.SwitchProtocols(headers, protocolSessionClosure)
+    }
+}
+
+public class WebSocketSession {
+    
+    public enum Error: ErrorProtocol { case UnknownOpCode(String), UnMaskedFrame }
+    public enum OpCode: UInt8 { case Continue = 0x00, Close = 0x08, Ping = 0x09, Pong = 0x0A, Text = 0x01, Binary = 0x02 }
+    
+    public class Frame {
+        public var opcode = OpCode.Close
+        public var fin = false
+        public var payload = [UInt8]()
+    }
+
+    private let socket: Socket
+    
+    public init(_ socket: Socket) {
+        self.socket = socket
+    }
+    
+    public func writeText(_ text: String) -> Void {
+        self.writeFrame(ArraySlice(text.utf8), OpCode.Text)
+    }
+
+    public func writeBinary(_ binary: [UInt8]) -> Void {
+        self.writeBinary(ArraySlice(binary))
+    }
+    
+    public func writeBinary(_ binary: ArraySlice<UInt8>) -> Void {
+        self.writeFrame(binary, OpCode.Binary)
+    }
+    
+    private func writeFrame(_ data: ArraySlice<UInt8>, _ op: OpCode, _ fin: Bool = true) {
+        let finAndOpCode = UInt8(fin ? 0x80 : 0x00) | op.rawValue
+        let maskAndLngth = encodeLengthAndMaskFlag(UInt64(data.count), false)
+        do {
+            try self.socket.writeUInt8([finAndOpCode])
+            try self.socket.writeUInt8(maskAndLngth)
+            try self.socket.writeUInt8(data)
+        } catch {
+            print(error)
+        }
+    }
+    
+    private func encodeLengthAndMaskFlag(_ len: UInt64, _ masked: Bool) -> [UInt8] {
+        let encodedLngth = UInt8(masked ? 0x80 : 0x00)
+        var encodedBytes = [UInt8]()
+        switch len {
+        case 0...125:
+            encodedBytes.append(encodedLngth | UInt8(len));
+        case 126...UInt64(UINT16_MAX):
+            encodedBytes.append(encodedLngth | 0x7E);
+            encodedBytes.append(UInt8(len >> 8));
+            encodedBytes.append(UInt8(len & 0xFF));
+        default:
+            encodedBytes.append(encodedLngth | 0x7F);
+            encodedBytes.append(UInt8(len >> 56) & 0xFF);
+            encodedBytes.append(UInt8(len >> 48) & 0xFF);
+            encodedBytes.append(UInt8(len >> 40) & 0xFF);
+            encodedBytes.append(UInt8(len >> 32) & 0xFF);
+            encodedBytes.append(UInt8(len >> 24) & 0xFF);
+            encodedBytes.append(UInt8(len >> 16) & 0xFF);
+            encodedBytes.append(UInt8(len >> 08) & 0xFF);
+            encodedBytes.append(UInt8(len >> 00) & 0xFF);
+        }
+        return encodedBytes
+    }
+    
+    public func readFrame() throws -> Frame {
+        let frm = Frame()
+        let fst = try socket.read()
+        frm.fin = fst & 0x80 != 0
+        let opc = fst & 0x0F
+        guard let opcode = OpCode(rawValue: opc) else {
+            // "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 )
+            throw Error.UnknownOpCode("\(opc)")
+        }
+        frm.opcode = opcode
+        let sec = try socket.read()
+        let msk = sec & 0x80 != 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 {
+            let b0 = UInt64(try socket.read())
+            let b1 = UInt64(try socket.read())
+            len = UInt64(littleEndian: b0 << 8 | b1)
+        } else if len == 0x7F {
+            let b0 = UInt64(try socket.read())
+            let b1 = UInt64(try socket.read())
+            let b2 = UInt64(try socket.read())
+            let b3 = UInt64(try socket.read())
+            let b4 = UInt64(try socket.read())
+            let b5 = UInt64(try socket.read())
+            let b6 = UInt64(try socket.read())
+            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 = [try socket.read(), try socket.read(), try socket.read(), try socket.read()]
+        for i in 0..<len {
+            frm.payload.append(try socket.read() ^ mask[Int(i % 4)])
+        }
+        return frm
+    }
+}

+ 24 - 30
XCode/Swifter.xcodeproj/project.pbxproj

@@ -27,15 +27,12 @@
 		7C3196071CC2C68F00DF5406 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195E91CC2C68F00DF5406 /* File.swift */; };
 		7C3196081CC2C68F00DF5406 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195E91CC2C68F00DF5406 /* File.swift */; };
 		7C3196091CC2C68F00DF5406 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195E91CC2C68F00DF5406 /* File.swift */; };
-		7C31960A1CC2C68F00DF5406 /* HttpHandlers+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EA1CC2C68F00DF5406 /* HttpHandlers+Files.swift */; };
-		7C31960B1CC2C68F00DF5406 /* HttpHandlers+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EA1CC2C68F00DF5406 /* HttpHandlers+Files.swift */; };
-		7C31960C1CC2C68F00DF5406 /* HttpHandlers+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EA1CC2C68F00DF5406 /* HttpHandlers+Files.swift */; };
-		7C31960D1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EB1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift */; };
-		7C31960E1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EB1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift */; };
-		7C31960F1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EB1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift */; };
-		7C3196101CC2C68F00DF5406 /* HttpHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EC1CC2C68F00DF5406 /* HttpHandlers.swift */; };
-		7C3196111CC2C68F00DF5406 /* HttpHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EC1CC2C68F00DF5406 /* HttpHandlers.swift */; };
-		7C3196121CC2C68F00DF5406 /* HttpHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EC1CC2C68F00DF5406 /* HttpHandlers.swift */; };
+		7C31960A1CC2C68F00DF5406 /* Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EA1CC2C68F00DF5406 /* Files.swift */; };
+		7C31960B1CC2C68F00DF5406 /* Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EA1CC2C68F00DF5406 /* Files.swift */; };
+		7C31960C1CC2C68F00DF5406 /* Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EA1CC2C68F00DF5406 /* Files.swift */; };
+		7C31960D1CC2C68F00DF5406 /* WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EB1CC2C68F00DF5406 /* WebSockets.swift */; };
+		7C31960E1CC2C68F00DF5406 /* WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EB1CC2C68F00DF5406 /* WebSockets.swift */; };
+		7C31960F1CC2C68F00DF5406 /* WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195EB1CC2C68F00DF5406 /* WebSockets.swift */; };
 		7C3196131CC2C68F00DF5406 /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195ED1CC2C68F00DF5406 /* HttpParser.swift */; };
 		7C3196141CC2C68F00DF5406 /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195ED1CC2C68F00DF5406 /* HttpParser.swift */; };
 		7C3196151CC2C68F00DF5406 /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195ED1CC2C68F00DF5406 /* HttpParser.swift */; };
@@ -75,8 +72,8 @@
 		7C31963A1CC2C68F00DF5406 /* String+SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195FA1CC2C68F00DF5406 /* String+SHA1.swift */; };
 		7C31963B1CC2C68F00DF5406 /* String+SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195FA1CC2C68F00DF5406 /* String+SHA1.swift */; };
 		7C31963C1CC2C68F00DF5406 /* String+SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3195FA1CC2C68F00DF5406 /* String+SHA1.swift */; };
-		7C3945641D256FDA003EEABA /* HttpHandlers+Scopes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3945631D256FDA003EEABA /* HttpHandlers+Scopes.swift */; };
-		7C3945651D256FDA003EEABA /* HttpHandlers+Scopes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3945631D256FDA003EEABA /* HttpHandlers+Scopes.swift */; };
+		7C3945641D256FDA003EEABA /* Scopes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3945631D256FDA003EEABA /* Scopes.swift */; };
+		7C3945651D256FDA003EEABA /* Scopes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3945631D256FDA003EEABA /* Scopes.swift */; };
 		7C4785E91C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4785E81C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift */; };
 		7C4785EA1C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4785E81C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift */; };
 		7C5915221C92A99300D884BC /* SwifterTestsReflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5915211C92A99300D884BC /* SwifterTestsReflection.swift */; };
@@ -88,6 +85,7 @@
 		7C73C6921C26179C00AEF6CA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CDAB80C1BE2A1D400C8A977 /* AppDelegate.swift */; };
 		7C7488781C1DA07300CBCD77 /* file.html in Resources */ = {isa = PBXBuildFile; fileRef = 7C7488771C1DA07300CBCD77 /* file.html */; };
 		7C74887B1C1DA08200CBCD77 /* file.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7C7488771C1DA07300CBCD77 /* file.html */; };
+		7C83177B1D2C5DF300630662 /* Scopes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3945631D256FDA003EEABA /* Scopes.swift */; };
 		7CA4813E19A2EA8D0030B30D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4813D19A2EA8D0030B30D /* main.swift */; };
 		7CA4815819A2EF2B0030B30D /* test.json in Resources */ = {isa = PBXBuildFile; fileRef = 7CA4815719A2EF2B0030B30D /* test.json */; };
 		7CA4815919A2EF560030B30D /* test.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7CA4815719A2EF2B0030B30D /* test.json */; };
@@ -182,9 +180,8 @@
 		7C3195E71CC2C68F00DF5406 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
 		7C3195E81CC2C68F00DF5406 /* DemoServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoServer.swift; sourceTree = "<group>"; };
 		7C3195E91CC2C68F00DF5406 /* File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
-		7C3195EA1CC2C68F00DF5406 /* HttpHandlers+Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HttpHandlers+Files.swift"; sourceTree = "<group>"; };
-		7C3195EB1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HttpHandlers+WebSockets.swift"; sourceTree = "<group>"; };
-		7C3195EC1CC2C68F00DF5406 /* HttpHandlers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpHandlers.swift; sourceTree = "<group>"; };
+		7C3195EA1CC2C68F00DF5406 /* Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Files.swift; sourceTree = "<group>"; };
+		7C3195EB1CC2C68F00DF5406 /* WebSockets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSockets.swift; sourceTree = "<group>"; };
 		7C3195ED1CC2C68F00DF5406 /* HttpParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpParser.swift; sourceTree = "<group>"; };
 		7C3195EE1CC2C68F00DF5406 /* HttpRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpRequest.swift; sourceTree = "<group>"; };
 		7C3195EF1CC2C68F00DF5406 /* HttpResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpResponse.swift; sourceTree = "<group>"; };
@@ -198,7 +195,7 @@
 		7C3195F81CC2C68F00DF5406 /* String+BASE64.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+BASE64.swift"; sourceTree = "<group>"; };
 		7C3195F91CC2C68F00DF5406 /* String+Misc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Misc.swift"; sourceTree = "<group>"; };
 		7C3195FA1CC2C68F00DF5406 /* String+SHA1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SHA1.swift"; sourceTree = "<group>"; };
-		7C3945631D256FDA003EEABA /* HttpHandlers+Scopes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HttpHandlers+Scopes.swift"; sourceTree = "<group>"; };
+		7C3945631D256FDA003EEABA /* Scopes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scopes.swift; sourceTree = "<group>"; };
 		7C4785E81C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsWebSocketSession.swift; sourceTree = "<group>"; };
 		7C5915211C92A99300D884BC /* SwifterTestsReflection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsReflection.swift; sourceTree = "<group>"; };
 		7C6B57EA1CA6C3AA0042655C /* SwifterTestsHttpRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsHttpRouter.swift; sourceTree = "<group>"; };
@@ -346,10 +343,9 @@
 				7C3195E71CC2C68F00DF5406 /* App.swift */,
 				7C3195E81CC2C68F00DF5406 /* DemoServer.swift */,
 				7C3195E91CC2C68F00DF5406 /* File.swift */,
-				7C3195EA1CC2C68F00DF5406 /* HttpHandlers+Files.swift */,
-				7C3945631D256FDA003EEABA /* HttpHandlers+Scopes.swift */,
-				7C3195EB1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift */,
-				7C3195EC1CC2C68F00DF5406 /* HttpHandlers.swift */,
+				7C3195EA1CC2C68F00DF5406 /* Files.swift */,
+				7C3945631D256FDA003EEABA /* Scopes.swift */,
+				7C3195EB1CC2C68F00DF5406 /* WebSockets.swift */,
 				7C3195ED1CC2C68F00DF5406 /* HttpParser.swift */,
 				7C3195EE1CC2C68F00DF5406 /* HttpRequest.swift */,
 				7C3195EF1CC2C68F00DF5406 /* HttpResponse.swift */,
@@ -794,7 +790,7 @@
 				7C3196131CC2C68F00DF5406 /* HttpParser.swift in Sources */,
 				7C31963A1CC2C68F00DF5406 /* String+SHA1.swift in Sources */,
 				7C31961F1CC2C68F00DF5406 /* HttpServer.swift in Sources */,
-				7C31960D1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift in Sources */,
+				7C31960D1CC2C68F00DF5406 /* WebSockets.swift in Sources */,
 				7C3195FE1CC2C68F00DF5406 /* sqlite.c in Sources */,
 				7C3196311CC2C68F00DF5406 /* SQLite.swift in Sources */,
 				7C3196341CC2C68F00DF5406 /* String+BASE64.swift in Sources */,
@@ -802,11 +798,10 @@
 				7C3196071CC2C68F00DF5406 /* File.swift in Sources */,
 				7C3196041CC2C68F00DF5406 /* DemoServer.swift in Sources */,
 				7C3196281CC2C68F00DF5406 /* Reflection.swift in Sources */,
-				7C3945641D256FDA003EEABA /* HttpHandlers+Scopes.swift in Sources */,
+				7C3945641D256FDA003EEABA /* Scopes.swift in Sources */,
 				7C3196161CC2C68F00DF5406 /* HttpRequest.swift in Sources */,
 				7C3196221CC2C68F00DF5406 /* HttpServerIO.swift in Sources */,
-				7C3196101CC2C68F00DF5406 /* HttpHandlers.swift in Sources */,
-				7C31960A1CC2C68F00DF5406 /* HttpHandlers+Files.swift in Sources */,
+				7C31960A1CC2C68F00DF5406 /* Files.swift in Sources */,
 				7C3196011CC2C68F00DF5406 /* App.swift in Sources */,
 				7C3196251CC2C68F00DF5406 /* Process.swift in Sources */,
 				7C3196371CC2C68F00DF5406 /* String+Misc.swift in Sources */,
@@ -822,7 +817,7 @@
 				7C3196141CC2C68F00DF5406 /* HttpParser.swift in Sources */,
 				7C31963B1CC2C68F00DF5406 /* String+SHA1.swift in Sources */,
 				7C3196201CC2C68F00DF5406 /* HttpServer.swift in Sources */,
-				7C31960E1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift in Sources */,
+				7C31960E1CC2C68F00DF5406 /* WebSockets.swift in Sources */,
 				7C3195FF1CC2C68F00DF5406 /* sqlite.c in Sources */,
 				7C3196321CC2C68F00DF5406 /* SQLite.swift in Sources */,
 				7C3196351CC2C68F00DF5406 /* String+BASE64.swift in Sources */,
@@ -830,11 +825,10 @@
 				7C3196081CC2C68F00DF5406 /* File.swift in Sources */,
 				7C3196051CC2C68F00DF5406 /* DemoServer.swift in Sources */,
 				7C3196291CC2C68F00DF5406 /* Reflection.swift in Sources */,
-				7C3945651D256FDA003EEABA /* HttpHandlers+Scopes.swift in Sources */,
+				7C3945651D256FDA003EEABA /* Scopes.swift in Sources */,
 				7C3196171CC2C68F00DF5406 /* HttpRequest.swift in Sources */,
 				7C3196231CC2C68F00DF5406 /* HttpServerIO.swift in Sources */,
-				7C3196111CC2C68F00DF5406 /* HttpHandlers.swift in Sources */,
-				7C31960B1CC2C68F00DF5406 /* HttpHandlers+Files.swift in Sources */,
+				7C31960B1CC2C68F00DF5406 /* Files.swift in Sources */,
 				7C3196021CC2C68F00DF5406 /* App.swift in Sources */,
 				7C3196261CC2C68F00DF5406 /* Process.swift in Sources */,
 				7C3196381CC2C68F00DF5406 /* String+Misc.swift in Sources */,
@@ -875,8 +869,9 @@
 				7C31961B1CC2C68F00DF5406 /* HttpResponse.swift in Sources */,
 				7C3196151CC2C68F00DF5406 /* HttpParser.swift in Sources */,
 				7C31963C1CC2C68F00DF5406 /* String+SHA1.swift in Sources */,
+				7C83177B1D2C5DF300630662 /* Scopes.swift in Sources */,
 				7C3196211CC2C68F00DF5406 /* HttpServer.swift in Sources */,
-				7C31960F1CC2C68F00DF5406 /* HttpHandlers+WebSockets.swift in Sources */,
+				7C31960F1CC2C68F00DF5406 /* WebSockets.swift in Sources */,
 				7C3196001CC2C68F00DF5406 /* sqlite.c in Sources */,
 				7C3196331CC2C68F00DF5406 /* SQLite.swift in Sources */,
 				7C3196361CC2C68F00DF5406 /* String+BASE64.swift in Sources */,
@@ -886,8 +881,7 @@
 				7C31962A1CC2C68F00DF5406 /* Reflection.swift in Sources */,
 				7C3196181CC2C68F00DF5406 /* HttpRequest.swift in Sources */,
 				7C3196241CC2C68F00DF5406 /* HttpServerIO.swift in Sources */,
-				7C3196121CC2C68F00DF5406 /* HttpHandlers.swift in Sources */,
-				7C31960C1CC2C68F00DF5406 /* HttpHandlers+Files.swift in Sources */,
+				7C31960C1CC2C68F00DF5406 /* Files.swift in Sources */,
 				7C3196031CC2C68F00DF5406 /* App.swift in Sources */,
 				7C3196271CC2C68F00DF5406 /* Process.swift in Sources */,
 				7C3196391CC2C68F00DF5406 /* String+Misc.swift in Sources */,

+ 18 - 18
XCode/SwifterTestsCommon/SwifterTestsWebSocketSession.swift

@@ -32,7 +32,7 @@ class SwifterTestsWebSocketSession: XCTestCase {
     func testParser() {
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0]))
+            let session = WebSocketSession(TestSocket([0]))
             try session.readFrame()
             XCTAssert(false, "Parser should throw an error if socket has not enough data for a frame.")
         } catch {
@@ -40,17 +40,17 @@ class SwifterTestsWebSocketSession: XCTestCase {
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b0000_0001, 0b0000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b0000_0001, 0b0000_0000, 0, 0, 0, 0]))
             try session.readFrame()
             XCTAssert(false, "Parser should not accept unmasked frames.")
-        } catch HttpHandlers.WebSocketSession.Error.UnMaskedFrame {
+        } catch WebSocketSession.Error.UnMaskedFrame {
             XCTAssert(true, "Parse should throw UnMaskedFrame error for unmasked message.")
         } catch {
             XCTAssert(false, "Parse should throw UnMaskedFrame error for unmasked message.")
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b1000_0001, 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b1000_0001, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
             XCTAssert(frame.fin, "Parser should detect fin flag set.")
         } catch {
@@ -58,59 +58,59 @@ class SwifterTestsWebSocketSession: XCTestCase {
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b0000_0000, 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b0000_0000, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, HttpHandlers.WebSocketSession.OpCode.Continue, "Parser should accept Continue opcode.")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Continue, "Parser should accept Continue opcode.")
         } catch {
             XCTAssertTrue(true, "Parser should accept Continue opcode without any errors.")
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b0000_0001, 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b0000_0001, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, HttpHandlers.WebSocketSession.OpCode.Text, "Parser should accept Text opcode.")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Text, "Parser should accept Text opcode.")
         } catch {
             XCTAssert(false, "Parser should accept Text opcode without any errors.")
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b0000_0010, 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b0000_0010, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, HttpHandlers.WebSocketSession.OpCode.Binary, "Parser should accept Binary opcode.")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Binary, "Parser should accept Binary opcode.")
         } catch {
             XCTAssert(false, "Parser should accept Binary opcode without any errors.")
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b0000_1000, 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b0000_1000, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, HttpHandlers.WebSocketSession.OpCode.Close, "Parser should accept Close opcode.")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Close, "Parser should accept Close opcode.")
         } catch {
             XCTAssert(false, "Parser should accept Close opcode without any errors.")
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b0000_1001, 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b0000_1001, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, HttpHandlers.WebSocketSession.OpCode.Ping, "Parser should accept Ping opcode.")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Ping, "Parser should accept Ping opcode.")
         } catch {
             XCTAssert(false, "Parser should accept Ping opcode without any errors.")
         }
         
         do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([0b0000_1010, 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([0b0000_1010, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, HttpHandlers.WebSocketSession.OpCode.Pong, "Parser should accept Pong opcode.")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Pong, "Parser should accept Pong opcode.")
         } catch {
             XCTAssert(false, "Parser should accept Pong opcode without any errors.")
         }
         
         for opcode in [3, 4, 5, 6, 7, 11, 12, 13, 14, 15] {
             do {
-            let session = HttpHandlers.WebSocketSession(TestSocket([UInt8(opcode), 0b1000_0000, 0, 0, 0, 0]))
+            let session = WebSocketSession(TestSocket([UInt8(opcode), 0b1000_0000, 0, 0, 0, 0]))
                 try session.readFrame()
                 XCTAssert(false, "Parse should throw an error for unknown opcode: \(opcode)")
-            } catch HttpHandlers.WebSocketSession.Error.UnknownOpCode(_) {
+            } catch WebSocketSession.Error.UnknownOpCode(_) {
                 XCTAssert(true, "Parse should throw UnknownOpCode error for unknown opcode.")
             } catch {
                 XCTAssert(false, "Parse should throw UnknownOpCode error for unknown opcode (was \(error)).")