Explorar el Código

Merge pull request #2 from httpswift/stable

Merge 3.0
Peter Zignego hace 9 años
padre
commit
d388edbb3c
Se han modificado 40 ficheros con 1082 adiciones y 1013 borrados
  1. 1 0
      .swift-version
  2. 18 4
      README.md
  3. 13 17
      Sources/DemoServer.swift
  4. 2 7
      Sources/Errno.swift
  5. 0 160
      Sources/File.swift
  6. 33 28
      Sources/Files.swift
  7. 11 15
      Sources/HttpParser.swift
  8. 23 27
      Sources/HttpRequest.swift
  9. 61 65
      Sources/HttpResponse.swift
  10. 36 22
      Sources/HttpRouter.swift
  11. 6 10
      Sources/HttpServer.swift
  12. 50 67
      Sources/HttpServerIO.swift
  13. 4 8
      Sources/Process.swift
  14. 163 165
      Sources/Scopes.swift
  15. 10 12
      Sources/Socket+File.swift
  16. 61 61
      Sources/Socket+Server.swift
  17. 56 76
      Sources/Socket.swift
  18. 5 9
      Sources/String+BASE64.swift
  19. 140 0
      Sources/String+File.swift
  20. 11 14
      Sources/String+Misc.swift
  21. 44 46
      Sources/String+SHA1.swift
  22. 60 63
      Sources/WebSockets.swift
  23. 2 2
      Swifter.podspec
  24. 61 34
      XCode/Swifter.xcodeproj/project.pbxproj
  25. 1 1
      XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifterMac.xcscheme
  26. 1 1
      XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifteriOS.xcscheme
  27. 1 1
      XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwiftertvOS.xcscheme
  28. 1 1
      XCode/SwifterMac/Info.plist
  29. 9 5
      XCode/SwifterSampleOSX/main.swift
  30. 2 3
      XCode/SwifterSampleiOS/AppDelegate.swift
  31. 1 1
      XCode/SwifterSampleiOS/Info.plist
  32. 1 1
      XCode/SwifterSampleiOS/ViewController.swift
  33. 11 8
      XCode/SwifterTestsCommon/IOSafetyTests.swift
  34. 19 20
      XCode/SwifterTestsCommon/PingServer.swift
  35. 25 26
      XCode/SwifterTestsCommon/SwifterTestsHttpParser.swift
  36. 108 0
      XCode/SwifterTestsCommon/SwifterTestsHttpRouter.swift
  37. 10 11
      XCode/SwifterTestsCommon/SwifterTestsStringExtensions.swift
  38. 19 20
      XCode/SwifterTestsCommon/SwifterTestsWebSocketSession.swift
  39. 1 1
      XCode/SwifteriOS/Info.plist
  40. 1 1
      XCode/SwiftertvOS/Info.plist

+ 1 - 0
.swift-version

@@ -0,0 +1 @@
+3.0

+ 18 - 4
README.md

@@ -15,7 +15,7 @@ Tiny http server engine written in Swift ( https://developer.apple.com/swift/ )
 ### How to start?
 ```swift
 let server = HttpServer()
-server["/hello"] = { .OK(.Html("You asked for " + $0.url)) }
+server["/hello"] = { .ok(.html("You asked for \($0)"))  }
 server.start()
 ```
 ### How to share files?
@@ -28,7 +28,7 @@ server.start()
 ```swift
 let server = HttpServer()
 server["/redirect"] = { request in
-  return .MovedPermanently("http://www.google.com")
+  return .movedPermanently("http://www.google.com")
 }
 server.start()
 ```
@@ -56,12 +56,26 @@ server.start()
 ```
 ### CocoaPods? Yes.
 ```
+# Use version >= 1.1.0.rc.2 (sudo gem install cocoapods --pre)
 use_frameworks!
-pod 'Swifter', '~> 1.2.6'
+pod 'Swifter', '~> 1.3.2'
 ```
 
 ### Carthage? Also yes.
+```
+# Use version >= 0.18 (https://github.com/Carthage/Carthage/releases/tag/0.18)
+github "glock45/swifter" == 1.3.2
+```
 
+### Swift Package Manager.
 ```
-github "glock45/swifter" == 1.2.6
+import PackageDescription
+
+let package = Package(
+    name: "MyServer",
+    dependencies: [
+        .Package(url: "https://github.com/httpswift/swifter.git", majorVersion: 1)
+    ]
+)
 ```
+

+ 13 - 17
Sources/DemoServer.swift

@@ -5,20 +5,16 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-public func demoServer(publicDir: String) -> HttpServer {
+
+public func demoServer(_ publicDir: String) -> HttpServer {
     
     print(publicDir)
     
     let server = HttpServer()
     
     server["/public/:path"] = shareFilesFromDirectory(publicDir)
-    server["/public/"] = shareFilesFromDirectory(publicDir)    // needed to serve index file at root level
 
     server["/files/:path"] = directoryBrowser("/")
 
@@ -34,7 +30,7 @@ public func demoServer(publicDir: String) -> HttpServer {
         }
     }
     
-    server["/magic"] = { .OK(.Html("You asked for " + $0.path)) }
+    server["/magic"] = { .ok(.html("You asked for " + $0.path)) }
     
     server["/test/:param1/:param2"] = { r in
         scopes {
@@ -101,7 +97,7 @@ public func demoServer(publicDir: String) -> HttpServer {
         for multipart in r.parseMultiPartFormData() {
             response += "Name: \(multipart.name) File name: \(multipart.fileName) Size: \(multipart.body.count)<br>"
         }
-        return HttpResponse.OK(.Html(response))
+        return HttpResponse.ok(.html(response))
     }
     
     server.GET["/login"] = scopes {
@@ -139,7 +135,7 @@ public func demoServer(publicDir: String) -> HttpServer {
     
     server.POST["/login"] = { r in
         let formFields = r.parseUrlencodedForm()
-        return HttpResponse.OK(.Html(formFields.map({ "\($0.0) = \($0.1)" }).joinWithSeparator("<br>")))
+        return HttpResponse.ok(.html(formFields.map({ "\($0.0) = \($0.1)" }).joined(separator: "<br>")))
     }
     
     server["/demo"] = scopes {
@@ -154,25 +150,25 @@ public func demoServer(publicDir: String) -> HttpServer {
     }
     
     server["/raw"] = { r in
-        return HttpResponse.RAW(200, "OK", ["XXX-Custom-Header": "value"], { try $0.write([UInt8]("test".utf8)) })
+        return HttpResponse.raw(200, "OK", ["XXX-Custom-Header": "value"], { try $0.write([UInt8]("test".utf8)) })
     }
     
     server["/redirect"] = { r in
-        return .MovedPermanently("http://www.google.com")
+        return .movedPermanently("http://www.google.com")
     }
 
     server["/long"] = { r in
         var longResponse = ""
         for k in 0..<1000 { longResponse += "(\(k)),->" }
-        return .OK(.Html(longResponse))
+        return .ok(.html(longResponse))
     }
     
     server["/wildcard/*/test/*/:param"] = { r in
-        return .OK(.Html(r.path))
+        return .ok(.html(r.path))
     }
     
     server["/stream"] = { r in
-        return HttpResponse.RAW(200, "OK", nil, { w in
+        return HttpResponse.raw(200, "OK", nil, { w in
             for i in 0...100 {
                 try w.write([UInt8]("[chunk \(i)]".utf8))
             }
@@ -181,12 +177,12 @@ public func demoServer(publicDir: String) -> HttpServer {
     
     server["/websocket-echo"] = websocket({ (session, text) in
         session.writeText(text)
-    }, { (session, binary) in
+        }, { (session, binary) in
         session.writeBinary(binary)
     })
     
     server.notFoundHandler = { r in
-        return .MovedPermanently("https://github.com/404")
+        return .movedPermanently("https://github.com/404")
     }
     
     server.middleware.append { r in

+ 2 - 7
Sources/Errno.swift

@@ -2,19 +2,14 @@
 //  Errno.swift
 //  Swifter
 //
-//  Created by Damian Kolakowski on 13/07/16.
 //  Copyright © 2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
 public class Errno {
     
     public class func description() -> String {
-        return String.fromCString(UnsafePointer(strerror(errno))) ?? "Error: \(errno)"
+        return String(cString: UnsafePointer(strerror(errno)))
     }
 }

+ 0 - 160
Sources/File.swift

@@ -1,160 +0,0 @@
-//
-//  File.swift
-//  Swifter
-//
-//  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
-//
-
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
-
-public enum FileError: ErrorType {
-    case OpenFailed(String)
-    case WriteFailed(String)
-    case ReadFailed(String)
-    case SeekFailed(String)
-    case GetCurrentWorkingDirectoryFailed(String)
-    case IsDirectoryFailed(String)
-    case OpenDirFailed(String)
-}
-
-public class File {
-    
-    public static func openNewForWriting(path: String) throws -> File {
-        return try openFileForMode(path, "wb")
-    }
-    
-    public static func openForReading(path: String) throws -> File {
-        return try openFileForMode(path, "rb")
-    }
-    
-    public static func openForWritingAndReading(path: String) throws -> File {
-        return try openFileForMode(path, "r+b")
-    }
-    
-    public static func openFileForMode(path: String, _ mode: String) throws -> File {
-        let file = path.withCString({ pathPointer in mode.withCString({ fopen(pathPointer, $0) }) })
-        guard file != nil else {
-            throw FileError.OpenFailed(Errno.description())
-        }
-        return File(file)
-    }
-    
-    public static func isDirectory(path: String) throws -> Bool {
-        var s = stat()
-        guard path.withCString({ stat($0, &s) }) == 0 else {
-            throw FileError.IsDirectoryFailed(Errno.description())
-        }
-        return s.st_mode & S_IFMT == S_IFDIR
-    }
-    
-    public static func currentWorkingDirectory() throws -> String {
-        let path = getcwd(nil, 0)
-        if path == nil {
-            throw FileError.GetCurrentWorkingDirectoryFailed(Errno.description())
-        }
-        guard let result = String.fromCString(path) else {
-            throw FileError.GetCurrentWorkingDirectoryFailed("Could not convert getcwd(...)'s result to String.")
-        }
-        return result
-    }
-    
-    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(Errno.description())
-        }
-        defer { closedir(dir) }
-        var results = [String]()
-        while case let ent = readdir(dir) where ent != nil {
-            var name = ent.memory.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: Int(NAME_MAX))))
-                #else
-                    var buffer = [CChar](UnsafeBufferPointer(start: unsafeBitCast(ptr, UnsafePointer<CChar>.self), count: Int(ent.memory.d_namlen)))
-                    buffer.append(0)
-                    return String.fromCString(buffer)
-                #endif
-            }
-            if let fileName = fileName {
-                results.append(fileName)
-            }
-        }
-        return results
-    }
-    
-    let pointer: UnsafeMutablePointer<FILE>
-    
-    public init(_ pointer: UnsafeMutablePointer<FILE>) {
-        self.pointer = pointer
-    }
-    
-    public func close() -> Void {
-        fclose(pointer)
-    }
-    
-    public func read(inout data: [UInt8]) throws -> Int {
-        if data.count <= 0 {
-            return data.count
-        }
-        let count = fread(&data, 1, data.count, self.pointer)
-        if count == data.count {
-            return count
-        }
-        if feof(self.pointer) != 0 {
-            return count
-        }
-        if ferror(self.pointer) != 0 {
-            throw FileError.ReadFailed(Errno.description())
-        }
-        throw FileError.ReadFailed("Unknown file read error occured.")
-    }
-
-    public func write(data: [UInt8]) throws -> Void {
-        if data.count <= 0 {
-            return
-        }
-        try data.withUnsafeBufferPointer {
-            if fwrite($0.baseAddress, 1, data.count, self.pointer) != data.count {
-                throw FileError.WriteFailed(Errno.description())
-            }
-        }
-    }
-    
-    public func seek(offset: Int) throws -> Void {
-        if fseek(self.pointer, offset, SEEK_SET) != 0 {
-            throw FileError.SeekFailed(Errno.description())
-        }
-    }
-
-}
-
-public func withNewFileOpenedForWriting<Result>(path: String, _ f: File throws -> Result) throws -> Result {
-    return try withFileOpenedForMode(path, mode: "wb", f)
-}
-
-public func withFileOpenedForReading<Result>(path: String, _ f: File throws -> Result) throws -> Result {
-    return try withFileOpenedForMode(path, mode: "rb", f)
-}
-
-public func withFileOpenedForWritingAndReading<Result>(path: String, _ f: File throws -> Result) throws -> Result {
-    return try withFileOpenedForMode(path, mode: "r+b", f)
-}
-
-public func withFileOpenedForMode<Result>(path: String, mode: String, _ f: File throws -> Result) throws -> Result {
-    let file = try File.openFileForMode(path, mode)
-    defer {
-        file.close()
-    }
-    return try f(file)
-}
-

+ 33 - 28
Sources/Files.swift

@@ -5,40 +5,45 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-public func shareFilesFromDirectory(directoryPath: String) -> (HttpRequest -> HttpResponse) {
+public func shareFilesFromDirectory(_ directoryPath: String, defaults: [String] = ["index.html", "default.html"]) -> ((HttpRequest) -> HttpResponse) {
     return { r in
         guard let fileRelativePath = r.params.first else {
-            return .NotFound
+            return .notFound
         }
-        let absolutePath = directoryPath + "/" + fileRelativePath.1
-        guard let file = try? File.openForReading(absolutePath) else {
-            return .NotFound
+        if fileRelativePath.value.isEmpty {
+            for path in defaults {
+                if let file = try? (directoryPath + String.pathSeparator + path).openForReading() {
+                    return .raw(200, "OK", [:], { writer in
+                        try? writer.write(file)
+                        file.close()
+                    })
+                }
+            }
+        }
+        if let file = try? (directoryPath + String.pathSeparator + fileRelativePath.value).openForReading() {
+            return .raw(200, "OK", [:], { writer in
+                try? writer.write(file)
+                file.close()
+            })
         }
-        return .RAW(200, "OK", [:], { writer in
-            defer { file.close() }
-            try writer.write(file)
-        })
+        return .notFound
     }
 }
 
-public func directoryBrowser(dir: String) -> (HttpRequest -> HttpResponse) {
+public func directoryBrowser(_ dir: String) -> ((HttpRequest) -> HttpResponse) {
     return { r in
         guard let (_, value) = r.params.first else {
-            return HttpResponse.NotFound
+            return HttpResponse.notFound
         }
-        let filePath = dir + "/" + value
+        let filePath = dir + String.pathSeparator + value
         do {
-            guard try File.exists(filePath) else {
-                return HttpResponse.NotFound
+            guard try filePath.exists() else {
+                return .notFound
             }
-            if try File.isDirectory(filePath) {
-                let files = try File.list(filePath)
+            if try filePath.directory() {
+                let files = try filePath.files()
                 return scopes {
                     html {
                         body {
@@ -54,18 +59,18 @@ public func directoryBrowser(dir: String) -> (HttpRequest -> HttpResponse) {
                             }
                         }
                     }
-                }(r)
+                    }(r)
             } else {
-                guard let file = try? File.openForReading(filePath) else {
-                    return .NotFound
+                guard let file = try? filePath.openForReading() else {
+                    return .notFound
                 }
-                return .RAW(200, "OK", [:], { writer in
-                    defer { file.close() }
-                    try writer.write(file)
+                return .raw(200, "OK", [:], { writer in
+                    try? writer.write(file)
+                    file.close()
                 })
             }
         } catch {
-            return HttpResponse.InternalServerError
+            return HttpResponse.internalServerError
         }
     }
 }

+ 11 - 15
Sources/HttpParser.swift

@@ -5,13 +5,9 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-enum HttpParserError: ErrorType {
+enum HttpParserError: Error {
     case InvalidStatusLine(String)
 }
 
@@ -19,7 +15,7 @@ public class HttpParser {
     
     public init() { }
     
-    public func readHttpRequest(socket: Socket) throws -> HttpRequest {
+    public func readHttpRequest(_ socket: Socket) throws -> HttpRequest {
         let statusLine = try socket.readLine()
         let statusLineTokens = statusLine.split(" ")
         if statusLineTokens.count < 3 {
@@ -36,37 +32,37 @@ public class HttpParser {
         return request
     }
     
-    private func extractQueryParams(url: String) -> [(String, String)] {
+    private func extractQueryParams(_ url: String) -> [(String, String)] {
         guard let query = url.split("?").last else {
             return []
         }
         return query.split("&").reduce([(String, String)]()) { (c, s) -> [(String, String)] in
             let tokens = s.split(1, separator: "=")
-            if let name = tokens.first, value = tokens.last {
+            if let name = tokens.first, let value = tokens.last {
                 return c + [(name.removePercentEncoding(), value.removePercentEncoding())]
             }
             return c
         }
     }
     
-    private func readBody(socket: Socket, size: Int) throws -> [UInt8] {
+    private func readBody(_ socket: Socket, size: Int) throws -> [UInt8] {
         var body = [UInt8]()
         for _ in 0..<size { body.append(try socket.read()) }
         return body
     }
     
-    private func readHeaders(socket: Socket) throws -> [String: String] {
+    private func readHeaders(_ socket: Socket) throws -> [String: String] {
         var headers = [String: String]()
-        while case let headerLine = try socket.readLine() where !headerLine.isEmpty {
+        while case let headerLine = try socket.readLine() , !headerLine.isEmpty {
             let headerTokens = headerLine.split(1, separator: ":")
-            if let name = headerTokens.first, value = headerTokens.last {
-                headers[name.lowercaseString] = value.trim()
+            if let name = headerTokens.first, let value = headerTokens.last {
+                headers[name.lowercased()] = value.trim()
             }
         }
         return headers
     }
     
-    func supportsKeepAlive(headers: [String: String]) -> Bool {
+    func supportsKeepAlive(_ headers: [String: String]) -> Bool {
         if let value = headers["connection"] {
             return "keep-alive" == value.trim()
         }

+ 23 - 27
Sources/HttpRequest.swift

@@ -5,11 +5,7 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
 public class HttpRequest {
     
@@ -21,11 +17,11 @@ public class HttpRequest {
     public var address: String? = ""
     public var params: [String: String] = [:]
     
-    public func hasTokenForHeader(headerName: String, token: String) -> Bool {
+    public func hasTokenForHeader(_ headerName: String, token: String) -> Bool {
         guard let headerValue = headers[headerName] else {
             return false
         }
-        return headerValue.split(",").filter({ $0.trim().lowercaseString == token }).count > 0
+        return headerValue.split(",").filter({ $0.trim().lowercased() == token }).count > 0
     }
     
     public func parseUrlencodedForm() -> [(String, String)] {
@@ -33,14 +29,14 @@ public class HttpRequest {
             return []
         }
         let contentTypeHeaderTokens = contentTypeHeader.split(";").map { $0.trim() }
-        guard let contentType = contentTypeHeaderTokens.first where contentType == "application/x-www-form-urlencoded" else {
+        guard let contentType = contentTypeHeaderTokens.first, contentType == "application/x-www-form-urlencoded" else {
             return []
         }
         return String.fromUInt8(body).split("&").map { param -> (String, String) in
             let tokens = param.split("=")
-            if let name = tokens.first, value = tokens.last where tokens.count == 2 {
-                return (name.replace("+", " ").removePercentEncoding(),
-                        value.replace("+", " ").removePercentEncoding())
+            if let name = tokens.first, let value = tokens.last, tokens.count == 2 {
+                return (name.replace(old: "+", " ").removePercentEncoding(),
+                        value.replace(old: "+", " ").removePercentEncoding())
             }
             return ("","")
         }
@@ -59,20 +55,20 @@ public class HttpRequest {
             return valueFor("content-disposition", parameter: "filename")?.unquote()
         }
         
-        private func valueFor(headerName: String, parameter: String) -> String? {
+        private func valueFor(_ headerName: String, parameter: String) -> String? {
             return headers.reduce([String]()) { (combined, header: (key: String, value: String)) -> [String] in
                 guard header.key == headerName else {
                     return combined
                 }
                 let headerValueParams = header.value.split(";").map { $0.trim() }
-                return headerValueParams.reduce(combined, combine: { (results, token) -> [String] in
+                return headerValueParams.reduce(combined, { (results, token) -> [String] in
                     let parameterTokens = token.split(1, separator: "=")
                     if parameterTokens.first == parameter, let value = parameterTokens.last {
                         return results + [value]
                     }
                     return results
                 })
-            }.first
+                }.first
         }
     }
     
@@ -81,24 +77,24 @@ public class HttpRequest {
             return []
         }
         let contentTypeHeaderTokens = contentTypeHeader.split(";").map { $0.trim() }
-        guard let contentType = contentTypeHeaderTokens.first where contentType == "multipart/form-data" else {
+        guard let contentType = contentTypeHeaderTokens.first, contentType == "multipart/form-data" else {
             return []
         }
         var boundary: String? = nil
         contentTypeHeaderTokens.forEach({
             let tokens = $0.split("=")
-            if let key = tokens.first where key == "boundary" && tokens.count == 2 {
+            if let key = tokens.first, key == "boundary" && tokens.count == 2 {
                 boundary = tokens.last
             }
         })
-        if let boundary = boundary where boundary.utf8.count > 0 {
+        if let boundary = boundary, boundary.utf8.count > 0 {
             return parseMultiPartFormData(body, boundary: "--\(boundary)")
         }
         return []
     }
     
-    private func parseMultiPartFormData(data: [UInt8], boundary: String) -> [MultiPart] {
-        var generator = data.generate()
+    private func parseMultiPartFormData(_ data: [UInt8], boundary: String) -> [MultiPart] {
+        var generator = data.makeIterator()
         var result = [MultiPart]()
         while let part = nextMultiPart(&generator, boundary: boundary, isFirst: result.isEmpty) {
             result.append(part)
@@ -106,19 +102,19 @@ public class HttpRequest {
         return result
     }
     
-    private func nextMultiPart(inout generator: IndexingGenerator<[UInt8]>, boundary: String, isFirst: Bool) -> MultiPart? {
+    private func nextMultiPart(_ generator: inout IndexingIterator<[UInt8]>, boundary: String, isFirst: Bool) -> MultiPart? {
         if isFirst {
             guard nextMultiPartLine(&generator) == boundary else {
                 return nil
             }
         } else {
-            nextMultiPartLine(&generator)
+            let /* ignore */ _ = nextMultiPartLine(&generator)
         }
         var headers = [String: String]()
-        while let line = nextMultiPartLine(&generator) where !line.isEmpty {
+        while let line = nextMultiPartLine(&generator), !line.isEmpty {
             let tokens = line.split(":")
-            if let name = tokens.first, value = tokens.last where tokens.count == 2 {
-                headers[name.lowercaseString] = value.trim()
+            if let name = tokens.first, let value = tokens.last, tokens.count == 2 {
+                headers[name.lowercased()] = value.trim()
             }
         }
         guard let body = nextMultiPartBody(&generator, boundary: boundary) else {
@@ -127,7 +123,7 @@ public class HttpRequest {
         return MultiPart(headers: headers, body: body)
     }
     
-    private func nextMultiPartLine(inout generator: IndexingGenerator<[UInt8]>) -> String? {
+    private func nextMultiPartLine(_ generator: inout IndexingIterator<[UInt8]>) -> String? {
         var result = String()
         while let value = generator.next() {
             if value > HttpRequest.CR {
@@ -143,7 +139,7 @@ public class HttpRequest {
     static let CR = UInt8(13)
     static let NL = UInt8(10)
     
-    private func nextMultiPartBody(inout generator: IndexingGenerator<[UInt8]>, boundary: String) -> [UInt8]? {
+    private func nextMultiPartBody(_ generator: inout IndexingIterator<[UInt8]>, boundary: String) -> [UInt8]? {
         var body = [UInt8]()
         let boundaryArray = [UInt8](boundary.utf8)
         var matchOffset = 0;
@@ -151,7 +147,7 @@ public class HttpRequest {
             matchOffset = ( x == boundaryArray[matchOffset] ? matchOffset + 1 : 0 )
             body.append(x)
             if matchOffset == boundaryArray.count {
-                body.removeRange(Range<Int>(body.count-matchOffset ..< body.count))
+                body.removeSubrange(CountableRange<Int>(body.count-matchOffset ..< body.count))
                 if body.last == HttpRequest.NL {
                     body.removeLast()
                     if body.last == HttpRequest.CR {

+ 61 - 65
Sources/HttpResponse.swift

@@ -5,62 +5,58 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-public enum SerializationError: ErrorType {
-    case InvalidObject
-    case NotSupported
+public enum SerializationError: Error {
+    case invalidObject
+    case notSupported
 }
 
 public protocol HttpResponseBodyWriter {
-    func write(file: File) throws
-    func write(data: [UInt8]) throws
-    func write(data: ArraySlice<UInt8>) throws
-    func write(data: NSData) throws
+    func write(_ file: String.File) throws
+    func write(_ data: [UInt8]) throws
+    func write(_ data: ArraySlice<UInt8>) throws
+    func write(_ data: NSData) throws
+    func write(_ data: Data) throws
 }
 
 public enum HttpResponseBody {
     
-    case Json(AnyObject)
-    case Html(String)
-    case Text(String)
-    case Custom(Any, (Any) throws -> String)
+    case json(AnyObject)
+    case html(String)
+    case text(String)
+    case custom(Any, (Any) throws -> String)
     
-    func content() -> (Int, (HttpResponseBodyWriter throws -> Void)?) {
+    func content() -> (Int, ((HttpResponseBodyWriter) throws -> Void)?) {
         do {
             switch self {
-            case .Json(let object):
+            case .json(let object):
                 #if os(Linux)
                     let data = [UInt8]("Not ready for Linux.".utf8)
                     return (data.count, {
                         try $0.write(data)
                     })
                 #else
-                    guard NSJSONSerialization.isValidJSONObject(object) else {
-                        throw SerializationError.InvalidObject
+                    guard JSONSerialization.isValidJSONObject(object) else {
+                        throw SerializationError.invalidObject
                     }
-                    let json = try NSJSONSerialization.dataWithJSONObject(object, options: NSJSONWritingOptions.PrettyPrinted)
-                    let data = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(json.bytes), count: json.length))
+                    let data = try JSONSerialization.data(withJSONObject: object)
                     return (data.count, {
                         try $0.write(data)
                     })
                 #endif
-            case .Text(let body):
+            case .text(let body):
                 let data = [UInt8](body.utf8)
                 return (data.count, {
                     try $0.write(data)
                 })
-            case .Html(let body):
+            case .html(let body):
                 let serialised = "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
                 let data = [UInt8](serialised.utf8)
                 return (data.count, {
                     try $0.write(data)
                 })
-            case .Custom(let object, let closure):
+            case .custom(let object, let closure):
                 let serialised = try closure(object)
                 let data = [UInt8](serialised.utf8)
                 return (data.count, {
@@ -78,61 +74,61 @@ public enum HttpResponseBody {
 
 public enum HttpResponse {
     
-    case SwitchProtocols([String: String], Socket -> Void)
-    case OK(HttpResponseBody), Created, Accepted
-    case MovedPermanently(String)
-    case BadRequest(HttpResponseBody?), Unauthorized, Forbidden, NotFound
-    case InternalServerError
-    case RAW(Int, String, [String:String]?, (HttpResponseBodyWriter throws -> Void)? )
+    case switchProtocols([String: String], (Socket) -> Void)
+    case ok(HttpResponseBody), created, accepted
+    case movedPermanently(String)
+    case badRequest(HttpResponseBody?), unauthorized, forbidden, notFound
+    case internalServerError
+    case raw(Int, String, [String:String]?, ((HttpResponseBodyWriter) throws -> Void)? )
 
     func statusCode() -> Int {
         switch self {
-        case .SwitchProtocols(_, _)   : return 101
-        case .OK(_)                   : return 200
-        case .Created                 : return 201
-        case .Accepted                : return 202
-        case .MovedPermanently        : return 301
-        case .BadRequest(_)           : return 400
-        case .Unauthorized            : return 401
-        case .Forbidden               : return 403
-        case .NotFound                : return 404
-        case .InternalServerError     : return 500
-        case .RAW(let code, _ , _, _) : return code
+        case .switchProtocols(_, _)   : return 101
+        case .ok(_)                   : return 200
+        case .created                 : return 201
+        case .accepted                : return 202
+        case .movedPermanently        : return 301
+        case .badRequest(_)           : return 400
+        case .unauthorized            : return 401
+        case .forbidden               : return 403
+        case .notFound                : return 404
+        case .internalServerError     : return 500
+        case .raw(let code, _ , _, _) : return code
         }
     }
     
     func reasonPhrase() -> String {
         switch self {
-        case .SwitchProtocols(_, _)    : return "Switching Protocols"
-        case .OK(_)                    : return "OK"
-        case .Created                  : return "Created"
-        case .Accepted                 : return "Accepted"
-        case .MovedPermanently         : return "Moved Permanently"
-        case .BadRequest(_)            : return "Bad Request"
-        case .Unauthorized             : return "Unauthorized"
-        case .Forbidden                : return "Forbidden"
-        case .NotFound                 : return "Not Found"
-        case .InternalServerError      : return "Internal Server Error"
-        case .RAW(_, let phrase, _, _) : return phrase
+        case .switchProtocols(_, _)    : return "Switching Protocols"
+        case .ok(_)                    : return "OK"
+        case .created                  : return "Created"
+        case .accepted                 : return "Accepted"
+        case .movedPermanently         : return "Moved Permanently"
+        case .badRequest(_)            : return "Bad Request"
+        case .unauthorized             : return "Unauthorized"
+        case .forbidden                : return "Forbidden"
+        case .notFound                 : return "Not Found"
+        case .internalServerError      : return "Internal Server Error"
+        case .raw(_, let phrase, _, _) : return phrase
         }
     }
     
     func headers() -> [String: String] {
         var headers = ["Server" : "Swifter \(HttpServer.VERSION)"]
         switch self {
-        case .SwitchProtocols(let switchHeaders, _):
+        case .switchProtocols(let switchHeaders, _):
             for (key, value) in switchHeaders {
                 headers[key] = value
             }
-        case .OK(let body):
+        case .ok(let body):
             switch body {
-            case .Json(_)   : headers["Content-Type"] = "application/json"
-            case .Html(_)   : headers["Content-Type"] = "text/html"
+            case .json(_)   : headers["Content-Type"] = "application/json"
+            case .html(_)   : headers["Content-Type"] = "text/html"
             default:break
             }
-        case .MovedPermanently(let location):
+        case .movedPermanently(let location):
             headers["Location"] = location
-        case .RAW(_, _, let rawHeaders, _):
+        case .raw(_, _, let rawHeaders, _):
             if let rawHeaders = rawHeaders {
                 for (k, v) in rawHeaders {
                     headers.updateValue(v, forKey: k)
@@ -143,18 +139,18 @@ public enum HttpResponse {
         return headers
     }
     
-    func content() -> (length: Int, write: (HttpResponseBodyWriter throws -> Void)?) {
+    func content() -> (length: Int, write: ((HttpResponseBodyWriter) throws -> Void)?) {
         switch self {
-        case .OK(let body)             : return body.content()
-        case .BadRequest(let body)     : return body?.content() ?? (-1, nil)
-        case .RAW(_, _, _, let writer) : return (-1, writer)
+        case .ok(let body)             : return body.content()
+        case .badRequest(let body)     : return body?.content() ?? (-1, nil)
+        case .raw(_, _, _, let writer) : return (-1, writer)
         default                        : return (-1, nil)
         }
     }
     
-    func socketSession() -> (Socket -> Void)?  {
+    func socketSession() -> ((Socket) -> Void)?  {
         switch self {
-        case SwitchProtocols(_, let handler) : return handler
+        case .switchProtocols(_, let handler) : return handler
         default: return nil
         }
     }

+ 36 - 22
Sources/HttpRouter.swift

@@ -5,17 +5,16 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-public class HttpRouter {
+open class HttpRouter {
+    
+    public init() {
+    }
     
     private class Node {
         var nodes = [String: Node]()
-        var handler: (HttpRequest -> HttpResponse)? = nil
+        var handler: ((HttpRequest) -> HttpResponse)? = nil
     }
     
     private var rootNode = Node()
@@ -23,44 +22,44 @@ public class HttpRouter {
     public func routes() -> [String] {
         var routes = [String]()
         for (_, child) in rootNode.nodes {
-            routes.appendContentsOf(routesForNode(child));
+            routes.append(contentsOf: routesForNode(child));
         }
         return routes
     }
     
-    private func routesForNode(node: Node, prefix: String = "") -> [String] {
+    private func routesForNode(_ node: Node, prefix: String = "") -> [String] {
         var result = [String]()
         if let _ = node.handler {
             result.append(prefix)
         }
         for (key, child) in node.nodes {
-            result.appendContentsOf(routesForNode(child, prefix: prefix + "/" + key));
+            result.append(contentsOf: routesForNode(child, prefix: prefix + "/" + key));
         }
         return result
     }
     
-    public func register(method: String?, path: String, handler: (HttpRequest -> HttpResponse)?) {
+    public func register(_ method: String?, path: String, handler: ((HttpRequest) -> HttpResponse)?) {
         var pathSegments = stripQuery(path).split("/")
         if let method = method {
-            pathSegments.insert(method, atIndex: 0)
+            pathSegments.insert(method, at: 0)
         } else {
-            pathSegments.insert("*", atIndex: 0)
+            pathSegments.insert("*", at: 0)
         }
-        var pathSegmentsGenerator = pathSegments.generate()
+        var pathSegmentsGenerator = pathSegments.makeIterator()
         inflate(&rootNode, generator: &pathSegmentsGenerator).handler = handler
     }
     
-    public func route(method: String?, path: String) -> ([String: String], HttpRequest -> HttpResponse)? {
+    public func route(_ method: String?, path: String) -> ([String: String], (HttpRequest) -> HttpResponse)? {
         if let method = method {
             let pathSegments = (method + "/" + stripQuery(path)).split("/")
-            var pathSegmentsGenerator = pathSegments.generate()
+            var pathSegmentsGenerator = pathSegments.makeIterator()
             var params = [String:String]()
             if let handler = findHandler(&rootNode, params: &params, generator: &pathSegmentsGenerator) {
                 return (params, handler)
             }
         }
         let pathSegments = ("*/" + stripQuery(path)).split("/")
-        var pathSegmentsGenerator = pathSegments.generate()
+        var pathSegmentsGenerator = pathSegments.makeIterator()
         var params = [String:String]()
         if let handler = findHandler(&rootNode, params: &params, generator: &pathSegmentsGenerator) {
             return (params, handler)
@@ -68,7 +67,7 @@ public class HttpRouter {
         return nil
     }
     
-    private func inflate(inout node: Node, inout generator: IndexingGenerator<[String]>) -> Node {
+    private func inflate(_ node: inout Node, generator: inout IndexingIterator<[String]>) -> Node {
         if let pathSegment = generator.next() {
             if let _ = node.nodes[pathSegment] {
                 return inflate(&node.nodes[pathSegment]!, generator: &generator)
@@ -80,8 +79,15 @@ public class HttpRouter {
         return node
     }
     
-    private func findHandler(inout node: Node, inout params: [String: String], inout generator: IndexingGenerator<[String]>) -> (HttpRequest -> HttpResponse)? {
+    private func findHandler(_ node: inout Node, params: inout [String: String], generator: inout IndexingIterator<[String]>) -> ((HttpRequest) -> HttpResponse)? {
         guard let pathToken = generator.next() else {
+            // if it's the last element of the requested URL, check if there is a pattern with variable tail.
+            if let variableNode = node.nodes.filter({ $0.0.characters.first == ":" }).first {
+                if variableNode.value.nodes.isEmpty {
+                    params[variableNode.0] = ""
+                    return variableNode.value.handler
+                }
+            }
             return node.handler
         }
         let variableNodes = node.nodes.filter { $0.0.characters.first == ":" }
@@ -89,8 +95,8 @@ public class HttpRouter {
             if variableNode.1.nodes.count == 0 {
                 // if it's the last element of the pattern and it's a variable, stop the search and
                 // append a tail as a value for the variable.
-                let tail = generator.joinWithSeparator("/")
-                if tail.utf8.count > 0 {
+                let tail = generator.joined(separator: "/")
+                if tail.characters.count > 0 {
                     params[variableNode.0] = pathToken + "/" + tail
                 } else {
                     params[variableNode.0] = pathToken
@@ -106,10 +112,18 @@ public class HttpRouter {
         if var node = node.nodes["*"] {
             return findHandler(&node, params: &params, generator: &generator)
         }
+        if let startStarNode = node.nodes["**"] {
+            let startStarNodeKeys = startStarNode.nodes.keys
+            while let pathToken = generator.next() {
+                if startStarNodeKeys.contains(pathToken) {
+                    return findHandler(&startStarNode.nodes[pathToken]!, params: &params, generator: &generator)
+                }
+            }
+        }
         return nil
     }
     
-    private func stripQuery(path: String) -> String {
+    private func stripQuery(_ path: String) -> String {
         if let path = path.split("?").first {
             return path
         }

+ 6 - 10
Sources/HttpServer.swift

@@ -5,15 +5,11 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
 public class HttpServer: HttpServerIO {
     
-    public static let VERSION = "1.2.6"
+    public static let VERSION = "1.3.2"
     
     private let router = HttpRouter()
     
@@ -36,7 +32,7 @@ public class HttpServer: HttpServerIO {
     public var DELETE, UPDATE, HEAD, POST, GET, PUT : MethodRoute
     public var delete, update, head, post, get, put : MethodRoute
     
-    public subscript(path: String) -> (HttpRequest -> HttpResponse)? {
+    public subscript(path: String) -> ((HttpRequest) -> HttpResponse)? {
         set {
             router.register(nil, path: path, handler: newValue)
         }
@@ -47,11 +43,11 @@ public class HttpServer: HttpServerIO {
         return router.routes();
     }
     
-    public var notFoundHandler: (HttpRequest -> HttpResponse)?
+    public var notFoundHandler: ((HttpRequest) -> HttpResponse)?
     
     public var middleware = Array<(HttpRequest) -> HttpResponse?>()
 
-    override public func dispatch(request: HttpRequest) -> ([String:String], HttpRequest -> HttpResponse) {
+    override public func dispatch(_ request: HttpRequest) -> ([String:String], (HttpRequest) -> HttpResponse) {
         for layer in middleware {
             if let response = layer(request) {
                 return ([:], { _ in response })
@@ -69,7 +65,7 @@ public class HttpServer: HttpServerIO {
     public struct MethodRoute {
         public let method: String
         public let router: HttpRouter
-        public subscript(path: String) -> (HttpRequest -> HttpResponse)? {
+        public subscript(path: String) -> ((HttpRequest) -> HttpResponse)? {
             set {
                 router.register(method, path: path, handler: newValue)
             }

+ 50 - 67
Sources/HttpServerIO.swift

@@ -5,27 +5,40 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
+import Dispatch
 
 public class HttpServerIO {
     
     private var socket = Socket(socketFileDescriptor: -1)
     private var sockets = Set<Socket>()
-    private var stateValue: Int32 = HttpServerIOState.Stopped.rawValue
+
+    public enum HttpServerIOState: Int32 {
+        case starting
+        case running
+        case stopping
+        case stopped
+    }
+    
+    private var stateValue: Int32 = HttpServerIOState.stopped.rawValue
+    
     public private(set) var state: HttpServerIOState {
         get {
             return HttpServerIOState(rawValue: stateValue)!
         }
         set(state) {
+            #if !os(Linux)
             OSAtomicCompareAndSwapInt(self.state.rawValue, state.rawValue, &stateValue)
+            #else
+            //TODO - hehe :)
+            self.stateValue = state.rawValue
+            #endif
         }
     }
-    public var operating: Bool { get { return self.state == .Running } }
-    private let queue = dispatch_queue_create("swifter.httpserverio.clientsockets", DISPATCH_QUEUE_SERIAL)
+    
+    public var operating: Bool { get { return self.state == .running } }
+    
+    private let queue = DispatchQueue(label: "swifter.httpserverio.clientsockets")
     
     public func port() throws -> Int {
         return Int(try socket.port())
@@ -39,51 +52,52 @@ public class HttpServerIO {
         stop()
     }
     
-    public func start(port: in_port_t = 8080, forceIPv4: Bool = false, priority: Int = DISPATCH_QUEUE_PRIORITY_BACKGROUND) throws {
+    @available(macOS 10.10, *)
+    public func start(_ port: in_port_t = 8080, forceIPv4: Bool = false, priority: DispatchQoS.QoSClass = DispatchQoS.QoSClass.background) throws {
         guard !self.operating else { return }
         stop()
-        self.state = .Starting
-        self.socket = try Socket.tcpSocketForListen(port, forceIPv4: forceIPv4)
-        dispatch_async(dispatch_get_global_queue(priority, 0)) { [weak self] in
+        self.state = .starting
+        self.socket = try Socket.tcpSocketForListen(port, forceIPv4)
+        DispatchQueue.global(qos: priority).async { [weak self] in
             guard let `self` = self else { return }
             guard self.operating else { return }
             while let socket = try? self.socket.acceptClientSocket() {
-                dispatch_async(dispatch_get_global_queue(priority, 0), { [weak self] in
+                DispatchQueue.global(qos: priority).async { [weak self] in
                     guard let `self` = self else { return }
                     guard self.operating else { return }
-                    dispatch_async(self.queue) {
+                    self.queue.async {
                         self.sockets.insert(socket)
                     }
                     self.handleConnection(socket)
-                    dispatch_async(self.queue) {
+                    self.queue.async {
                         self.sockets.remove(socket)
                     }
-                })
+                }
             }
             self.stop()
         }
-        self.state = .Running
+        self.state = .running
     }
     
     public func stop() {
         guard self.operating else { return }
-        self.state = .Stopping
+        self.state = .stopping
         // Shutdown connected peers because they can live in 'keep-alive' or 'websocket' loops.
         for socket in self.sockets {
-            socket.shutdwn()
+            socket.close()
         }
-        dispatch_sync(queue) {
-            self.sockets.removeAll(keepCapacity: true)
+        self.queue.sync {
+            self.sockets.removeAll(keepingCapacity: true)
         }
-        socket.release()
-        self.state = .Stopped
+        socket.close()
+        self.state = .stopped
     }
     
-    public func dispatch(request: HttpRequest) -> ([String: String], HttpRequest -> HttpResponse) {
-        return ([:], { _ in HttpResponse.NotFound })
+    public func dispatch(_ request: HttpRequest) -> ([String: String], (HttpRequest) -> HttpResponse) {
+        return ([:], { _ in HttpResponse.notFound })
     }
     
-    private func handleConnection(socket: Socket) {
+    private func handleConnection(_ socket: Socket) {
         let parser = HttpParser()
         while self.operating, let request = try? parser.readHttpRequest(socket) {
             let request = request
@@ -106,31 +120,35 @@ public class HttpServerIO {
             }
             if !keepConnection { break }
         }
-        socket.release()
+        socket.close()
     }
     
     private struct InnerWriteContext: HttpResponseBodyWriter {
         
         let socket: Socket
 
-        func write(file: File) throws {
+        func write(_ file: String.File) throws {
             try socket.writeFile(file)
         }
 
-        func write(data: [UInt8]) throws {
+        func write(_ data: [UInt8]) throws {
             try write(ArraySlice(data))
         }
 
-        func write(data: ArraySlice<UInt8>) throws {
+        func write(_ data: ArraySlice<UInt8>) throws {
             try socket.writeUInt8(data)
         }
 
-        func write(data: NSData) throws {
+        func write(_ data: NSData) throws {
+            try socket.writeData(data)
+        }
+        
+        func write(_ data: Data) throws {
             try socket.writeData(data)
         }
     }
     
-    private func respond(socket: Socket, response: HttpResponse, keepAlive: Bool) throws -> Bool {
+    private func respond(_ socket: Socket, response: HttpResponse, keepAlive: Bool) throws -> Bool {
         guard self.operating else { return false }
 
         try socket.writeUTF8("HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
@@ -159,38 +177,3 @@ public class HttpServerIO {
         return keepAlive && content.length != -1;
     }
 }
-
-public enum HttpServerIOState: Int32 {
-    case Starting
-    case Running
-    case Stopping
-    case Stopped
-}
-
-#if os(Linux)
-    
-let DISPATCH_QUEUE_PRIORITY_BACKGROUND = 0
-
-private class dispatch_context {
-    let block: ((Void) -> Void)
-    init(_ block: ((Void) -> Void)) {
-        self.block = block
-    }
-}
-
-func dispatch_get_global_queue(queueId: Int, _ arg: Int) -> Int { return 0 }
-
-func dispatch_async(queueId: Int, _ block: ((Void) -> Void)) {
-    let unmanagedDispatchContext = Unmanaged.passRetained(dispatch_context(block))
-    let context = UnsafeMutablePointer<Void>(unmanagedDispatchContext.toOpaque())
-    var pthread: pthread_t = 0
-    pthread_create(&pthread, nil, { (context: UnsafeMutablePointer<Void>) -> UnsafeMutablePointer<Void> in
-        let unmanaged = Unmanaged<dispatch_context>.fromOpaque(COpaquePointer(context))
-        unmanaged.takeUnretainedValue().block()
-        unmanaged.release()
-        return context
-    }, context)
-}
-    
-#endif
-

+ 4 - 8
Sources/Process.swift

@@ -5,19 +5,15 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
 public class Process {
     
-    public static var PID: Int {
+    public static var pid: Int {
         return Int(getpid())
     }
     
-    public static var TID: UInt64 {
+    public static var tid: UInt64 {
         #if os(Linux)
             return UInt64(pthread_self())
         #else
@@ -30,7 +26,7 @@ public class Process {
     private static var signalsWatchers = Array<(Int32) -> Void>()
     private static var signalsObserved = false
     
-    public static func watchSignals(callback: (Int32) -> Void) {
+    public static func watchSignals(_ callback: @escaping (Int32) -> Void) {
         if !signalsObserved {
             [SIGTERM, SIGHUP, SIGSTOP, SIGINT].forEach { item in
                 signal(item) {

+ 163 - 165
Sources/Scopes.swift

@@ -5,17 +5,15 @@
 //  Copyright © 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-public func scopes(scope: Closure) -> ((HttpRequest) -> HttpResponse) {
+public func scopes(_ scope: @escaping Closure) -> ((HttpRequest) -> HttpResponse) {
     return { r in
-        ScopesBuffer[Process.TID] = ""
+        ScopesBuffer[Process.tid] = ""
         scope()
-        return .RAW(200, "OK", ["Content-Type": "text/html"], { try $0.write([UInt8](("<!DOCTYPE html>"  + (ScopesBuffer[Process.TID] ?? "")).utf8)) })
+        return .raw(200, "OK", ["Content-Type": "text/html"], {
+            try? $0.write([UInt8](("<!DOCTYPE html>"  + (ScopesBuffer[Process.tid] ?? "")).utf8))
+        })
     }
 }
 
@@ -146,31 +144,31 @@ public var acceptCharset: String? = nil
 
 public var inner: String? = nil
 
-public func a(c: Closure) { element("a", c) }
-public func b(c: Closure) { element("b", c) }
-public func i(c: Closure) { element("i", c) }
-public func p(c: Closure) { element("p", c) }
-public func q(c: Closure) { element("q", c) }
-public func s(c: Closure) { element("s", c) }
-public func u(c: Closure) { element("u", c) }
+public func a(_ c: Closure) { element("a", c) }
+public func b(_ c: Closure) { element("b", c) }
+public func i(_ c: Closure) { element("i", c) }
+public func p(_ c: Closure) { element("p", c) }
+public func q(_ c: Closure) { element("q", c) }
+public func s(_ c: Closure) { element("s", c) }
+public func u(_ c: Closure) { element("u", c) }
 
-public func br(c: Closure) { element("br", c) }
-public func dd(c: Closure) { element("dd", c) }
-public func dl(c: Closure) { element("dl", c) }
-public func dt(c: Closure) { element("dt", c) }
-public func em(c: Closure) { element("em", c) }
-public func hr(c: Closure) { element("hr", c) }
-public func li(c: Closure) { element("li", c) }
-public func ol(c: Closure) { element("ol", c) }
-public func rp(c: Closure) { element("rp", c) }
-public func rt(c: Closure) { element("rt", c) }
-public func td(c: Closure) { element("td", c) }
-public func th(c: Closure) { element("th", c) }
-public func tr(c: Closure) { element("tr", c) }
-public func tt(c: Closure) { element("tt", c) }
-public func ul(c: Closure) { element("ul", c) }
+public func br(_ c: Closure) { element("br", c) }
+public func dd(_ c: Closure) { element("dd", c) }
+public func dl(_ c: Closure) { element("dl", c) }
+public func dt(_ c: Closure) { element("dt", c) }
+public func em(_ c: Closure) { element("em", c) }
+public func hr(_ c: Closure) { element("hr", c) }
+public func li(_ c: Closure) { element("li", c) }
+public func ol(_ c: Closure) { element("ol", c) }
+public func rp(_ c: Closure) { element("rp", c) }
+public func rt(_ c: Closure) { element("rt", c) }
+public func td(_ c: Closure) { element("td", c) }
+public func th(_ c: Closure) { element("th", c) }
+public func tr(_ c: Closure) { element("tr", c) }
+public func tt(_ c: Closure) { element("tt", c) }
+public func ul(_ c: Closure) { element("ul", c) }
 
-public func ul<T: SequenceType>(collection: T, _ c: (T.Generator.Element) -> Void) {
+public func ul<T: Sequence>(_ collection: T, _ c: @escaping (T.Iterator.Element) -> Void) {
     element("ul", {
         for item in collection {
             c(item)
@@ -178,72 +176,72 @@ public func ul<T: SequenceType>(collection: T, _ c: (T.Generator.Element) -> Voi
     })
 }
 
-public func h1(c: Closure) { element("h1", c) }
-public func h2(c: Closure) { element("h2", c) }
-public func h3(c: Closure) { element("h3", c) }
-public func h4(c: Closure) { element("h4", c) }
-public func h5(c: Closure) { element("h5", c) }
-public func h6(c: Closure) { element("h6", c) }
+public func h1(_ c: Closure) { element("h1", c) }
+public func h2(_ c: Closure) { element("h2", c) }
+public func h3(_ c: Closure) { element("h3", c) }
+public func h4(_ c: Closure) { element("h4", c) }
+public func h5(_ c: Closure) { element("h5", c) }
+public func h6(_ c: Closure) { element("h6", c) }
 
-public func bdi(c: Closure) { element("bdi", c) }
-public func bdo(c: Closure) { element("bdo", c) }
-public func big(c: Closure) { element("big", c) }
-public func col(c: Closure) { element("col", c) }
-public func del(c: Closure) { element("del", c) }
-public func dfn(c: Closure) { element("dfn", c) }
-public func dir(c: Closure) { element("dir", c) }
-public func div(c: Closure) { element("div", c) }
-public func img(c: Closure) { element("img", c) }
-public func ins(c: Closure) { element("ins", c) }
-public func kbd(c: Closure) { element("kbd", c) }
-public func map(c: Closure) { element("map", c) }
-public func nav(c: Closure) { element("nav", c) }
-public func pre(c: Closure) { element("pre", c) }
-public func rtc(c: Closure) { element("rtc", c) }
-public func sub(c: Closure) { element("sub", c) }
-public func sup(c: Closure) { element("sup", c) }
+public func bdi(_ c: Closure) { element("bdi", c) }
+public func bdo(_ c: Closure) { element("bdo", c) }
+public func big(_ c: Closure) { element("big", c) }
+public func col(_ c: Closure) { element("col", c) }
+public func del(_ c: Closure) { element("del", c) }
+public func dfn(_ c: Closure) { element("dfn", c) }
+public func dir(_ c: Closure) { element("dir", c) }
+public func div(_ c: Closure) { element("div", c) }
+public func img(_ c: Closure) { element("img", c) }
+public func ins(_ c: Closure) { element("ins", c) }
+public func kbd(_ c: Closure) { element("kbd", c) }
+public func map(_ c: Closure) { element("map", c) }
+public func nav(_ c: Closure) { element("nav", c) }
+public func pre(_ c: Closure) { element("pre", c) }
+public func rtc(_ c: Closure) { element("rtc", c) }
+public func sub(_ c: Closure) { element("sub", c) }
+public func sup(_ c: Closure) { element("sup", c) }
 
-public func varr(c: Closure) { element("var", c) }
-public func wbr(c: Closure) { element("wbr", c) }
-public func xmp(c: Closure) { element("xmp", c) }
+public func varr(_ c: Closure) { element("var", c) }
+public func wbr(_ c: Closure) { element("wbr", c) }
+public func xmp(_ c: Closure) { element("xmp", c) }
 
-public func abbr(c: Closure) { element("abbr", c) }
-public func area(c: Closure) { element("area", c) }
-public func base(c: Closure) { element("base", c) }
-public func body(c: Closure) { element("body", c) }
-public func cite(c: Closure) { element("cite", c) }
-public func code(c: Closure) { element("code", c) }
-public func data(c: Closure) { element("data", c) }
-public func font(c: Closure) { element("font", c) }
-public func form(c: Closure) { element("form", c) }
-public func head(c: Closure) { element("head", c) }
-public func html(c: Closure) { element("html", c) }
-public func link(c: Closure) { element("link", c) }
-public func main(c: Closure) { element("main", c) }
-public func mark(c: Closure) { element("mark", c) }
-public func menu(c: Closure) { element("menu", c) }
-public func meta(c: Closure) { element("meta", c) }
-public func nobr(c: Closure) { element("nobr", c) }
-public func ruby(c: Closure) { element("ruby", c) }
-public func samp(c: Closure) { element("samp", c) }
-public func span(c: Closure) { element("span", c) }
-public func time(c: Closure) { element("time", c) }
+public func abbr(_ c: Closure) { element("abbr", c) }
+public func area(_ c: Closure) { element("area", c) }
+public func base(_ c: Closure) { element("base", c) }
+public func body(_ c: Closure) { element("body", c) }
+public func cite(_ c: Closure) { element("cite", c) }
+public func code(_ c: Closure) { element("code", c) }
+public func data(_ c: Closure) { element("data", c) }
+public func font(_ c: Closure) { element("font", c) }
+public func form(_ c: Closure) { element("form", c) }
+public func head(_ c: Closure) { element("head", c) }
+public func html(_ c: Closure) { element("html", c) }
+public func link(_ c: Closure) { element("link", c) }
+public func main(_ c: Closure) { element("main", c) }
+public func mark(_ c: Closure) { element("mark", c) }
+public func menu(_ c: Closure) { element("menu", c) }
+public func meta(_ c: Closure) { element("meta", c) }
+public func nobr(_ c: Closure) { element("nobr", c) }
+public func ruby(_ c: Closure) { element("ruby", c) }
+public func samp(_ c: Closure) { element("samp", c) }
+public func span(_ c: Closure) { element("span", c) }
+public func time(_ c: Closure) { element("time", c) }
 
-public func aside(c: Closure) { element("aside", c) }
-public func audio(c: Closure) { element("audio", c) }
-public func blink(c: Closure) { element("blink", c) }
-public func embed(c: Closure) { element("embed", c) }
-public func frame(c: Closure) { element("frame", c) }
-public func image(c: Closure) { element("image", c) }
-public func input(c: Closure) { element("input", c) }
-public func label(c: Closure) { element("label", c) }
-public func meter(c: Closure) { element("meter", c) }
-public func param(c: Closure) { element("param", c) }
-public func small(c: Closure) { element("small", c) }
-public func style(c: Closure) { element("style", c) }
-public func table(c: Closure) { element("table", c) }
+public func aside(_ c: Closure) { element("aside", c) }
+public func audio(_ c: Closure) { element("audio", c) }
+public func blink(_ c: Closure) { element("blink", c) }
+public func embed(_ c: Closure) { element("embed", c) }
+public func frame(_ c: Closure) { element("frame", c) }
+public func image(_ c: Closure) { element("image", c) }
+public func input(_ c: Closure) { element("input", c) }
+public func label(_ c: Closure) { element("label", c) }
+public func meter(_ c: Closure) { element("meter", c) }
+public func param(_ c: Closure) { element("param", c) }
+public func small(_ c: Closure) { element("small", c) }
+public func style(_ c: Closure) { element("style", c) }
+public func table(_ c: Closure) { element("table", c) }
 
-public func table<T: SequenceType>(collection: T, c: (T.Generator.Element) -> Void) {
+public func table<T: Sequence>(_ collection: T, c: @escaping (T.Iterator.Element) -> Void) {
     element("table", {
         for item in collection {
             c(item)
@@ -251,9 +249,9 @@ public func table<T: SequenceType>(collection: T, c: (T.Generator.Element) -> Vo
     })
 }
 
-public func tbody(c: Closure) { element("tbody", c) }
+public func tbody(_ c: Closure) { element("tbody", c) }
 
-public func tbody<T: SequenceType>(collection: T, c: (T.Generator.Element) -> Void) {
+public func tbody<T: Sequence>(_ collection: T, c: @escaping (T.Iterator.Element) -> Void) {
     element("tbody", {
         for item in collection {
             c(item)
@@ -261,79 +259,79 @@ public func tbody<T: SequenceType>(collection: T, c: (T.Generator.Element) -> Vo
     })
 }
 
-public func tfoot(c: Closure) { element("tfoot", c) }
-public func thead(c: Closure) { element("thead", c) }
-public func title(c: Closure) { element("title", c) }
-public func track(c: Closure) { element("track", c) }
-public func video(c: Closure) { element("video", c) }
+public func tfoot(_ c: Closure) { element("tfoot", c) }
+public func thead(_ c: Closure) { element("thead", c) }
+public func title(_ c: Closure) { element("title", c) }
+public func track(_ c: Closure) { element("track", c) }
+public func video(_ c: Closure) { element("video", c) }
 
-public func applet(c: Closure) { element("applet", c) }
-public func button(c: Closure) { element("button", c) }
-public func canvas(c: Closure) { element("canvas", c) }
-public func center(c: Closure) { element("center", c) }
-public func dialog(c: Closure) { element("dialog", c) }
-public func figure(c: Closure) { element("figure", c) }
-public func footer(c: Closure) { element("footer", c) }
-public func header(c: Closure) { element("header", c) }
-public func hgroup(c: Closure) { element("hgroup", c) }
-public func iframe(c: Closure) { element("iframe", c) }
-public func keygen(c: Closure) { element("keygen", c) }
-public func legend(c: Closure) { element("legend", c) }
-public func object(c: Closure) { element("object", c) }
-public func option(c: Closure) { element("option", c) }
-public func output(c: Closure) { element("output", c) }
-public func script(c: Closure) { element("script", c) }
-public func select(c: Closure) { element("select", c) }
-public func shadow(c: Closure) { element("shadow", c) }
-public func source(c: Closure) { element("source", c) }
-public func spacer(c: Closure) { element("spacer", c) }
-public func strike(c: Closure) { element("strike", c) }
-public func strong(c: Closure) { element("strong", c) }
+public func applet(_ c: Closure) { element("applet", c) }
+public func button(_ c: Closure) { element("button", c) }
+public func canvas(_ c: Closure) { element("canvas", c) }
+public func center(_ c: Closure) { element("center", c) }
+public func dialog(_ c: Closure) { element("dialog", c) }
+public func figure(_ c: Closure) { element("figure", c) }
+public func footer(_ c: Closure) { element("footer", c) }
+public func header(_ c: Closure) { element("header", c) }
+public func hgroup(_ c: Closure) { element("hgroup", c) }
+public func iframe(_ c: Closure) { element("iframe", c) }
+public func keygen(_ c: Closure) { element("keygen", c) }
+public func legend(_ c: Closure) { element("legend", c) }
+public func object(_ c: Closure) { element("object", c) }
+public func option(_ c: Closure) { element("option", c) }
+public func output(_ c: Closure) { element("output", c) }
+public func script(_ c: Closure) { element("script", c) }
+public func select(_ c: Closure) { element("select", c) }
+public func shadow(_ c: Closure) { element("shadow", c) }
+public func source(_ c: Closure) { element("source", c) }
+public func spacer(_ c: Closure) { element("spacer", c) }
+public func strike(_ c: Closure) { element("strike", c) }
+public func strong(_ c: Closure) { element("strong", c) }
 
-public func acronym(c: Closure) { element("acronym", c) }
-public func address(c: Closure) { element("address", c) }
-public func article(c: Closure) { element("article", c) }
-public func bgsound(c: Closure) { element("bgsound", c) }
-public func caption(c: Closure) { element("caption", c) }
-public func command(c: Closure) { element("command", c) }
-public func content(c: Closure) { element("content", c) }
-public func details(c: Closure) { element("details", c) }
-public func elementt(c: Closure) { element("element", c) }
-public func isindex(c: Closure) { element("isindex", c) }
-public func listing(c: Closure) { element("listing", c) }
-public func marquee(c: Closure) { element("marquee", c) }
-public func noembed(c: Closure) { element("noembed", c) }
-public func picture(c: Closure) { element("picture", c) }
-public func section(c: Closure) { element("section", c) }
-public func summary(c: Closure) { element("summary", c) }
+public func acronym(_ c: Closure) { element("acronym", c) }
+public func address(_ c: Closure) { element("address", c) }
+public func article(_ c: Closure) { element("article", c) }
+public func bgsound(_ c: Closure) { element("bgsound", c) }
+public func caption(_ c: Closure) { element("caption", c) }
+public func command(_ c: Closure) { element("command", c) }
+public func content(_ c: Closure) { element("content", c) }
+public func details(_ c: Closure) { element("details", c) }
+public func elementt(_ c: Closure) { element("element", c) }
+public func isindex(_ c: Closure) { element("isindex", c) }
+public func listing(_ c: Closure) { element("listing", c) }
+public func marquee(_ c: Closure) { element("marquee", c) }
+public func noembed(_ c: Closure) { element("noembed", c) }
+public func picture(_ c: Closure) { element("picture", c) }
+public func section(_ c: Closure) { element("section", c) }
+public func summary(_ c: Closure) { element("summary", c) }
 
-public func basefont(c: Closure) { element("basefont", c) }
-public func colgroup(c: Closure) { element("colgroup", c) }
-public func datalist(c: Closure) { element("datalist", c) }
-public func fieldset(c: Closure) { element("fieldset", c) }
-public func frameset(c: Closure) { element("frameset", c) }
-public func menuitem(c: Closure) { element("menuitem", c) }
-public func multicol(c: Closure) { element("multicol", c) }
-public func noframes(c: Closure) { element("noframes", c) }
-public func noscript(c: Closure) { element("noscript", c) }
-public func optgroup(c: Closure) { element("optgroup", c) }
-public func progress(c: Closure) { element("progress", c) }
-public func template(c: Closure) { element("template", c) }
-public func textarea(c: Closure) { element("textarea", c) }
+public func basefont(_ c: Closure) { element("basefont", c) }
+public func colgroup(_ c: Closure) { element("colgroup", c) }
+public func datalist(_ c: Closure) { element("datalist", c) }
+public func fieldset(_ c: Closure) { element("fieldset", c) }
+public func frameset(_ c: Closure) { element("frameset", c) }
+public func menuitem(_ c: Closure) { element("menuitem", c) }
+public func multicol(_ c: Closure) { element("multicol", c) }
+public func noframes(_ c: Closure) { element("noframes", c) }
+public func noscript(_ c: Closure) { element("noscript", c) }
+public func optgroup(_ c: Closure) { element("optgroup", c) }
+public func progress(_ c: Closure) { element("progress", c) }
+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) }
+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) }
 
-public func stylesheet(c: Closure) { element("link", ["rel": "stylesheet", "type": "text/css"], c) }
+public func stylesheet(_ c: Closure) { element("link", ["rel": "stylesheet", "type": "text/css"], c) }
 
-public func element(node: String, _ c: Closure) { evaluate(node, [:], c) }
-public func element(node: String, _ attrs: [String: String?] = [:], _ c: Closure) { evaluate(node, attrs, c) }
+public func element(_ node: String, _ c: Closure) { evaluate(node, [:], c) }
+public func element(_ node: String, _ attrs: [String: String?] = [:], _ c: Closure) { evaluate(node, attrs, c) }
 
 var ScopesBuffer = [UInt64: String]()
 
-private func evaluate(node: String, _ attrs: [String: String?] = [:], _ c: Closure) {
+private func evaluate(_ node: String, _ attrs: [String: String?] = [:], _ c: Closure) {
     
     // Push the attributes.
     
@@ -585,15 +583,15 @@ private func evaluate(node: String, _ attrs: [String: String?] = [:], _ c: Closu
     acceptCharset = nil
     inner = nil
     
-    ScopesBuffer[Process.TID] = (ScopesBuffer[Process.TID] ?? "") + "<" + node
+    ScopesBuffer[Process.tid] = (ScopesBuffer[Process.tid] ?? "") + "<" + node
     
     // Save the current output before the nested scope evalutation.
     
-    var output = ScopesBuffer[Process.TID] ?? ""
+    var output = ScopesBuffer[Process.tid] ?? ""
     
     // Clear the output buffer for the evalutation.
     
-    ScopesBuffer[Process.TID] = ""
+    ScopesBuffer[Process.tid] = ""
     
     // Evaluate the nested scope.
     
@@ -725,7 +723,7 @@ private func evaluate(node: String, _ attrs: [String: String?] = [:], _ c: Closu
     if let marginheight = marginheight { mergedAttributes["marginheight"] = marginheight }
     if let acceptCharset = acceptCharset { mergedAttributes["accept-charset"] = acceptCharset }
     
-    for item in attrs.enumerate() {
+    for item in attrs.enumerated() {
         mergedAttributes.updateValue(item.element.1, forKey: item.element.0)
     }
     
@@ -738,10 +736,10 @@ private func evaluate(node: String, _ attrs: [String: String?] = [:], _ c: Closu
     }
     
     if let inner = inner {
-        ScopesBuffer[Process.TID] = output + ">" + (inner) + "</" + node + ">"
+        ScopesBuffer[Process.tid] = output + ">" + (inner) + "</" + node + ">"
     } else {
-        let current = ScopesBuffer[Process.TID]  ?? ""
-        ScopesBuffer[Process.TID] = output + ">" + current + "</" + node + ">"
+        let current = ScopesBuffer[Process.tid]  ?? ""
+        ScopesBuffer[Process.tid] = output + ">" + current + "</" + node + ">"
     }
     
     // Pop the attributes.

+ 10 - 12
Sources/Socket+File.swift

@@ -5,17 +5,14 @@
 //  Created by Damian Kolakowski on 13/07/16.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-#if os(iOS) || os (Linux)
+#if os(iOS) || os(Linux)
+    
     struct sf_hdtr { }
     
-    private func sendfileImpl(source: Int32, _ target: Int32, _: off_t, _: UnsafeMutablePointer<off_t>, _: UnsafeMutablePointer<sf_hdtr>, _: Int32) -> Int32 {
-        var buffer = [UInt8](count: 1024, repeatedValue: 0)
+    private func sendfileImpl(_ source: Int32, _ target: Int32, _: off_t, _: UnsafeMutablePointer<off_t>, _: UnsafeMutablePointer<sf_hdtr>, _: Int32) -> Int32 {
+        var buffer = [UInt8](repeating: 0, count: 1024)
         while true {
             let readResult = read(source, &buffer, buffer.count)
             guard readResult > 0 else {
@@ -33,16 +30,17 @@
     }
 #else
     private let sendfileImpl = sendfile
-
 #endif
 
 extension Socket {
     
-    public func writeFile(file: File) throws -> Void {
+    public func writeFile(_ file: String.File) throws -> Void {
         var offset: off_t = 0
-        let result = sendfileImpl(fileno(file.pointer), self.socketFileDescriptor, 0, &offset, nil, 0)
+        var sf: sf_hdtr = sf_hdtr()
+        let result = sendfileImpl(fileno(file.pointer), self.socketFileDescriptor, 0, &offset, &sf
+            , 0)
         if result == -1 {
-            throw SocketError.WriteFailed("sendfile: " + Errno.description())
+            throw SocketError.writeFailed("sendfile: " + Errno.description())
         }
     }
     

+ 61 - 61
Sources/Socket+Server.swift

@@ -5,15 +5,11 @@
 //  Created by Damian Kolakowski on 13/07/16.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
 extension Socket {
     
-    public class func tcpSocketForListen(port: in_port_t, forceIPv4: Bool = false, maxPendingConnection: Int32 = SOMAXCONN) throws -> Socket {
+    public class func tcpSocketForListen(_ port: in_port_t, _ forceIPv4: Bool = false, _ maxPendingConnection: Int32 = SOMAXCONN) throws -> Socket {
         
         #if os(Linux)
             let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, Int32(SOCK_STREAM.rawValue), 0)
@@ -22,77 +18,81 @@ extension Socket {
         #endif
         
         if socketFileDescriptor == -1 {
-            throw SocketError.SocketCreationFailed(Errno.description())
+            throw SocketError.socketCreationFailed(Errno.description())
         }
         
         var value: Int32 = 1
-        if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32))) == -1 {
+        if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(MemoryLayout<Int32>.size)) == -1 {
             let details = Errno.description()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.SocketSettingReUseAddrFailed(details)
+            Socket.close(socketFileDescriptor)
+            throw SocketError.socketSettingReUseAddrFailed(details)
         }
         Socket.setNoSigPipe(socketFileDescriptor)
         
-        #if os(Linux)
-            var bindResult: Int32 = -1
-            if forceIPv4 {
-                var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
-                                       sin_port: port.bigEndian,
-                                       sin_addr: in_addr(s_addr: in_addr_t(0)),
-                                       sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
-                
-                bindResult = withUnsafePointer(&addr) {
-                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in)))
-                }
-            } else {
-                var addr = sockaddr_in6(sin6_family: sa_family_t(AF_INET6),
-                                        sin6_port: port.bigEndian,
-                                        sin6_flowinfo: 0,
-                                        sin6_addr: in6addr_any,
-                                        sin6_scope_id: 0)
-                
-                bindResult = withUnsafePointer(&addr) {
-                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6)))
-                }
+
+        var bindResult: Int32 = -1
+        if forceIPv4 {
+            #if os(Linux)
+            var addr = sockaddr_in(
+                sin_family: sa_family_t(AF_INET),
+                sin_port: port.bigEndian,
+                sin_addr: in_addr(s_addr: in_addr_t(0)),
+                sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
+            #else
+            var addr = sockaddr_in(
+                sin_len: UInt8(MemoryLayout<sockaddr_in>.stride),
+                sin_family: UInt8(AF_INET),
+                sin_port: port.bigEndian,
+                sin_addr: in_addr(s_addr: in_addr_t(0)),
+                sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
+            #endif
+            bindResult = withUnsafePointer(to: &addr) {
+                bind(socketFileDescriptor, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in>.size))
             }
-        #else
-            var bindResult: Int32 = -1
-            if forceIPv4 {
-                var addr = sockaddr_in(sin_len: UInt8(strideof(sockaddr_in)),
-                                       sin_family: UInt8(AF_INET),
-                                       sin_port: port.bigEndian,
-                                       sin_addr: in_addr(s_addr: in_addr_t(0)),
-                                       sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
-                
-                bindResult = withUnsafePointer(&addr) {
-                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in)))
-                }
-            } else {
-                var addr = sockaddr_in6(sin6_len: UInt8(strideof(sockaddr_in6)),
-                                        sin6_family: UInt8(AF_INET6),
-                                        sin6_port: port.bigEndian,
-                                        sin6_flowinfo: 0,
-                                        sin6_addr: in6addr_any,
-                                        sin6_scope_id: 0)
-                
-                bindResult = withUnsafePointer(&addr) {
-                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6)))
-                }
+        } else {
+            #if os(Linux)
+            var addr = sockaddr_in6(
+                sin6_family: sa_family_t(AF_INET6),
+                sin6_port: port.bigEndian,
+                sin6_flowinfo: 0,
+                sin6_addr: in6addr_any,
+                sin6_scope_id: 0)
+            #else
+            var addr = sockaddr_in6(
+                sin6_len: UInt8(MemoryLayout<sockaddr_in6>.stride),
+                sin6_family: UInt8(AF_INET6),
+                sin6_port: port.bigEndian,
+                sin6_flowinfo: 0,
+                sin6_addr: in6addr_any,
+                sin6_scope_id: 0)
+            #endif
+            bindResult = withUnsafePointer(to: &addr) {
+                bind(socketFileDescriptor, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in6>.size))
             }
-        #endif
+        }
         
         if bindResult == -1 {
             let details = Errno.description()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.BindFailed(details)
+            Socket.close(socketFileDescriptor)
+            throw SocketError.bindFailed(details)
         }
         
-        if listen(socketFileDescriptor, maxPendingConnection ) == -1 {
+        if listen(socketFileDescriptor, maxPendingConnection) == -1 {
             let details = Errno.description()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.ListenFailed(details)
+            Socket.close(socketFileDescriptor)
+            throw SocketError.listenFailed(details)
         }
         return Socket(socketFileDescriptor: socketFileDescriptor)
     }
-
+    
+    public func acceptClientSocket() throws -> Socket {
+        var addr = sockaddr()
+        var len: socklen_t = 0
+        let clientSocket = accept(self.socketFileDescriptor, &addr, &len)
+        if clientSocket == -1 {
+            throw SocketError.acceptFailed(Errno.description())
+        }
+        Socket.setNoSigPipe(clientSocket)
+        return Socket(socketFileDescriptor: clientSocket)
+    }
 }

+ 56 - 76
Sources/Socket.swift

@@ -5,67 +5,53 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
-/* Low level routines for POSIX sockets */
 
-public enum SocketError: ErrorType {
-    case SocketCreationFailed(String)
-    case SocketSettingReUseAddrFailed(String)
-    case BindFailed(String)
-    case ListenFailed(String)
-    case WriteFailed(String)
-    case GetPeerNameFailed(String)
-    case ConvertingPeerNameFailed
-    case GetNameInfoFailed(String)
-    case AcceptFailed(String)
-    case RecvFailed(String)
-    case GetSockNameFailed(String)
+public enum SocketError: Error {
+    case socketCreationFailed(String)
+    case socketSettingReUseAddrFailed(String)
+    case bindFailed(String)
+    case listenFailed(String)
+    case writeFailed(String)
+    case getPeerNameFailed(String)
+    case convertingPeerNameFailed
+    case getNameInfoFailed(String)
+    case acceptFailed(String)
+    case recvFailed(String)
+    case getSockNameFailed(String)
 }
 
-public class Socket: Hashable, Equatable {
+open class Socket: Hashable, Equatable {
         
     let socketFileDescriptor: Int32
+    private var shutdown = false
+
     
     public init(socketFileDescriptor: Int32) {
         self.socketFileDescriptor = socketFileDescriptor
     }
     
     deinit {
-        shutdwn()
+        close()
     }
     
     public var hashValue: Int { return Int(self.socketFileDescriptor) }
     
-    public func release() {
-        Socket.release(self.socketFileDescriptor)
-    }
-    
-    public func shutdwn() {
-        Socket.shutdwn(self.socketFileDescriptor)
-    }
-    
-    public func acceptClientSocket() throws -> Socket {
-        var addr = sockaddr()        
-        var len: socklen_t = 0
-        let clientSocket = accept(self.socketFileDescriptor, &addr, &len)
-        if clientSocket == -1 {
-            throw SocketError.AcceptFailed(Errno.description())
+    public func close() {
+        if shutdown {
+            return
         }
-        Socket.setNoSigPipe(clientSocket)
-        return Socket(socketFileDescriptor: clientSocket)
+        shutdown = true
+        Socket.close(self.socketFileDescriptor)
     }
     
     public func port() throws -> in_port_t {
         var addr = sockaddr_in()
-        return try withUnsafePointer(&addr) { pointer in
-            var len = socklen_t(sizeof(sockaddr_in))
-            if getsockname(socketFileDescriptor, UnsafeMutablePointer(pointer), &len) != 0 {
-                throw SocketError.GetSockNameFailed(Errno.description())
+        return try withUnsafePointer(to: &addr) { pointer in
+            var len = socklen_t(MemoryLayout<sockaddr_in>.size)
+            if getsockname(socketFileDescriptor, UnsafeMutablePointer(OpaquePointer(pointer)), &len) != 0 {
+                throw SocketError.getSockNameFailed(Errno.description())
             }
             #if os(Linux)
                 return ntohs(addr.sin_port)
@@ -77,34 +63,40 @@ public class Socket: Hashable, Equatable {
     
     public func isIPv4() throws -> Bool {
         var addr = sockaddr_in()
-        return try withUnsafePointer(&addr) { pointer in
-            var len = socklen_t(sizeof(sockaddr_in))
-            if getsockname(socketFileDescriptor, UnsafeMutablePointer(pointer), &len) != 0 {
-                throw SocketError.GetSockNameFailed(Errno.description())
+        return try withUnsafePointer(to: &addr) { pointer in
+            var len = socklen_t(MemoryLayout<sockaddr_in>.size)
+            if getsockname(socketFileDescriptor, UnsafeMutablePointer(OpaquePointer(pointer)), &len) != 0 {
+                throw SocketError.getSockNameFailed(Errno.description())
             }
             return Int32(addr.sin_family) == AF_INET
         }
     }
     
-    public func writeUTF8(string: String) throws {
+    public func writeUTF8(_ string: String) throws {
         try writeUInt8(ArraySlice(string.utf8))
     }
     
-    public func writeUInt8(data: [UInt8]) throws {
+    public func writeUInt8(_ data: [UInt8]) throws {
         try writeUInt8(ArraySlice(data))
     }
     
-    public func writeUInt8(data: ArraySlice<UInt8>) throws {
+    public func writeUInt8(_ data: ArraySlice<UInt8>) throws {
         try data.withUnsafeBufferPointer {
-            try writeBuffer($0.baseAddress, length: data.count)
+            try writeBuffer($0.baseAddress!, length: data.count)
         }
     }
 
-    public func writeData(data: NSData) throws {
+    public func writeData(_ data: NSData) throws {
         try writeBuffer(data.bytes, length: data.length)
     }
+    
+    public func writeData(_ data: Data) throws {
+        try data.withUnsafeBytes { (pointer: UnsafePointer<UInt8>) -> Void in
+            try self.writeBuffer(pointer, length: data.count)
+        }
+    }
 
-    private func writeBuffer(pointer: UnsafePointer<Void>, length: Int) throws {
+    private func writeBuffer(_ pointer: UnsafeRawPointer, length: Int) throws {
         var sent = 0
         while sent < length {
             #if os(Linux)
@@ -113,17 +105,17 @@ public class Socket: Hashable, Equatable {
                 let s = write(self.socketFileDescriptor, pointer + sent, Int(length - sent))
             #endif
             if s <= 0 {
-                throw SocketError.WriteFailed(Errno.description())
+                throw SocketError.writeFailed(Errno.description())
             }
             sent += s
         }
     }
     
-    public func read() throws -> UInt8 {
-        var buffer = [UInt8](count: 1, repeatedValue: 0)
+    open func read() throws -> UInt8 {
+        var buffer = [UInt8](repeating: 0, count: 1)
         let next = recv(self.socketFileDescriptor as Int32, &buffer, Int(buffer.count), 0)
         if next <= 0 {
-            throw SocketError.RecvFailed(Errno.description())
+            throw SocketError.recvFailed(Errno.description())
         }
         return buffer[0]
     }
@@ -142,46 +134,34 @@ public class Socket: Hashable, Equatable {
     }
     
     public func peername() throws -> String {
-        var addr = sockaddr(), len: socklen_t = socklen_t(sizeof(sockaddr))
+        var addr = sockaddr(), len: socklen_t = socklen_t(MemoryLayout<sockaddr>.size)
         if getpeername(self.socketFileDescriptor, &addr, &len) != 0 {
-            throw SocketError.GetPeerNameFailed(Errno.description())
+            throw SocketError.getPeerNameFailed(Errno.description())
         }
-        var hostBuffer = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
+        var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
         if getnameinfo(&addr, len, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST) != 0 {
-            throw SocketError.GetNameInfoFailed(Errno.description())
-        }
-        guard let name = String.fromCString(hostBuffer) else {
-            throw SocketError.ConvertingPeerNameFailed
+            throw SocketError.getNameInfoFailed(Errno.description())
         }
-        return name
+        return String(cString: hostBuffer)
     }
     
-    public class func setNoSigPipe(socket: Int32) {
+    public class func setNoSigPipe(_ socket: Int32) {
         #if os(Linux)
             // There is no SO_NOSIGPIPE in Linux (nor some other systems). You can instead use the MSG_NOSIGNAL flag when calling send(),
             // or use signal(SIGPIPE, SIG_IGN) to make your entire application ignore SIGPIPE.
         #else
             // Prevents crashes when blocking calls are pending and the app is paused ( via Home button ).
             var no_sig_pipe: Int32 = 1
-            setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &no_sig_pipe, socklen_t(sizeof(Int32)))
-        #endif
-    }
-    
-    public class func shutdwn(socket: Int32) {
-        #if os(Linux)
-            shutdown(socket, Int32(SHUT_RDWR))
-        #else
-            Darwin.shutdown(socket, SHUT_RDWR)
+            setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &no_sig_pipe, socklen_t(MemoryLayout<Int32>.size))
         #endif
     }
     
-    public class func release(socket: Int32) {
+    public class func close(_ socket: Int32) {
         #if os(Linux)
-            shutdown(socket, Int32(SHUT_RDWR))
+            let _ = Glibc.close(socket)
         #else
-            Darwin.shutdown(socket, SHUT_RDWR)
+            let _ = Darwin.close(socket)
         #endif
-        close(socket)
     }
 }
 

+ 5 - 9
Sources/String+BASE64.swift

@@ -5,24 +5,20 @@
 //  Copyright © 2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
 
 extension String {
     
     private static let CODES = [UInt8]("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".utf8)
     
-    public static func toBase64(data: [UInt8]) -> String {
+    public static func toBase64(_ data: [UInt8]) -> String {
         
         // Based on: https://en.wikipedia.org/wiki/Base64#Sample_Implementation_in_Java
         
         var result = [UInt8]()
         var tmp: UInt8
-        for index in 0.stride(to: data.count, by: 3) {
+        for index in stride(from: 0, to: data.count, by: 3) {
             let byte = data[index]
             tmp = (byte & 0xFC) >> 2;
             result.append(CODES[Int(tmp)])
@@ -38,11 +34,11 @@ extension String {
                     result.append(CODES[Int(tmp)]);
                 } else  {
                     result.append(CODES[Int(tmp)]);
-                    result.appendContentsOf([UInt8]("=".utf8));
+                    result.append(contentsOf: [UInt8]("=".utf8));
                 }
             } else {
                 result.append(CODES[Int(tmp)]);
-                result.appendContentsOf([UInt8]("==".utf8));
+                result.append(contentsOf: [UInt8]("==".utf8));
             }
         }
         return String.fromUInt8(result)

+ 140 - 0
Sources/String+File.swift

@@ -0,0 +1,140 @@
+//
+//  String+File.swift
+//  Swifter
+//
+//  Copyright © 2016 Damian Kołakowski. All rights reserved.
+//
+
+import Foundation
+
+
+extension String {
+    
+    public enum FileError: Error {
+        case error(Int32)
+    }
+    
+    public class File {
+        
+        let pointer: UnsafeMutablePointer<FILE>
+        
+        public init(_ pointer: UnsafeMutablePointer<FILE>) {
+            self.pointer = pointer
+        }
+        
+        public func close() -> Void {
+            fclose(pointer)
+        }
+        
+        public func read(_ data: inout [UInt8]) throws -> Int {
+            if data.count <= 0 {
+                return data.count
+            }
+            let count = fread(&data, 1, data.count, self.pointer)
+            if count == data.count {
+                return count
+            }
+            if feof(self.pointer) != 0 {
+                return count
+            }
+            if ferror(self.pointer) != 0 {
+                throw FileError.error(errno)
+            }
+            throw FileError.error(0)
+        }
+        
+        public func write(_ data: [UInt8]) throws -> Void {
+            if data.count <= 0 {
+                return
+            }
+            try data.withUnsafeBufferPointer {
+                if fwrite($0.baseAddress, 1, data.count, self.pointer) != data.count {
+                    throw FileError.error(errno)
+                }
+            }
+        }
+        
+        public static func currentWorkingDirectory() throws -> String {
+            guard let path = getcwd(nil, 0) else {
+                throw FileError.error(errno)
+            }
+            return String(cString: path)
+        }
+    }
+    
+    public static var pathSeparator = "/"
+    
+    public func openNewForWriting() throws -> File {
+        return try openFileForMode(self, "wb")
+    }
+    
+    public func openForReading() throws -> File {
+        return try openFileForMode(self, "rb")
+    }
+    
+    public func openForWritingAndReading() throws -> File {
+        return try openFileForMode(self, "r+b")
+    }
+    
+    public func openFileForMode(_ path: String, _ mode: String) throws -> File {
+        guard let file = path.withCString({ pathPointer in mode.withCString({ fopen(pathPointer, $0) }) }) else {
+            throw FileError.error(errno)
+        }
+        return File(file)
+    }
+    
+    public func exists() throws -> Bool {
+        return try self.withStat {
+            if let _ = $0 {
+                return true
+            }
+            return false
+        }
+    }
+    
+    public func directory() throws -> Bool {
+        return try self.withStat {
+            if let stat = $0 {
+                return stat.st_mode & S_IFMT == S_IFDIR
+            }
+            return false
+        }
+    }
+    
+    public func files() throws -> [String] {
+        guard let dir = self.withCString({ opendir($0) }) else {
+            throw FileError.error(errno)
+        }
+        defer { closedir(dir) }
+        var results = [String]()
+        while let ent = readdir(dir) {
+            var name = ent.pointee.d_name
+            let fileName = withUnsafePointer(to: &name) { (ptr) -> String? in
+                #if os(Linux)
+                    return String(validatingUTF8: [CChar](UnsafeBufferPointer<CChar>(start: UnsafePointer(unsafeBitCast(ptr, to: 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
+    }
+    
+    private func withStat<T>(_ closure: ((stat?) throws -> T)) throws -> T {
+        return try self.withCString({
+            var statBuffer = stat()
+            if stat($0, &statBuffer) == 0 {
+                return try closure(statBuffer)
+            }
+            if errno == ENOENT {
+                return try closure(nil)
+            }
+            throw FileError.error(errno)
+        })
+    }
+}

+ 11 - 14
Sources/String+Misc.swift

@@ -5,20 +5,17 @@
 //  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
+
 
 extension String {
 
-    public func split(separator: Character) -> [String] {
+    public func split(_ separator: Character) -> [String] {
         return self.characters.split { $0 == separator }.map(String.init)
     }
     
-    public func split(maxSplit: Int = Int.max, separator: Character) -> [String] {
-        return self.characters.split(maxSplit) { $0 == separator }.map(String.init)
+    public func split(_ maxSplit: Int = Int.max, separator: Character) -> [String] {
+        return self.characters.split(maxSplits: maxSplit, omittingEmptySubsequences: true) { $0 == separator }.map(String.init)
     }
     
     public func replace(old: Character, _ new: Character) -> String {
@@ -44,9 +41,9 @@ extension String {
         return String(scalars)
     }
     
-    public static func fromUInt8(array: [UInt8]) -> String {
+    public static func fromUInt8(_ array: [UInt8]) -> String {
         // Apple changes the definition of String(data: .... ) every release so let's stay with 'fromUInt8(...)' wrapper.
-        return array.reduce("", combine: { $0.0 + String(UnicodeScalar($0.1)) })
+        return array.reduce("", { $0.0 + String(UnicodeScalar($0.1)) })
     }
     
     public func removePercentEncoding() -> String {
@@ -57,11 +54,11 @@ extension String {
             if scalar == "%" {
                 let first = scalars.popFirst()
                 let secon = scalars.popFirst()
-                if let first = first?.asAlpha(), secon = secon?.asAlpha() {
+                if let first = first?.asAlpha(), let secon = secon?.asAlpha() {
                     decodeBuffer.append(first*16+secon)
                 } else {
                     if !decodeBuffer.isEmpty {
-                        output.appendContentsOf(String.fromUInt8(decodeBuffer))
+                        output.append(String.fromUInt8(decodeBuffer))
                         decodeBuffer.removeAll()
                     }
                     if let first = first { output.append(Character(first)) }
@@ -69,14 +66,14 @@ extension String {
                 }
             } else {
                 if !decodeBuffer.isEmpty {
-                    output.appendContentsOf(String.fromUInt8(decodeBuffer))
+                    output.append(String.fromUInt8(decodeBuffer))
                     decodeBuffer.removeAll()
                 }
                 output.append(Character(scalar))
             }
         }
         if !decodeBuffer.isEmpty {
-            output.appendContentsOf(String.fromUInt8(decodeBuffer))
+            output.append(String.fromUInt8(decodeBuffer))
             decodeBuffer.removeAll()
         }
         return output

+ 44 - 46
Sources/String+SHA1.swift

@@ -5,24 +5,16 @@
 //  Copyright 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
 
 
-extension String {
-    
-    public func SHA1() -> String {
-        return SHA1().reduce("") { $0 + String(format: "%02x", $1) }
-    }
+public struct SHA1 {
     
-    public func SHA1() -> [UInt8] {
+    public static func hash(_ input: [UInt8]) -> [UInt8] {
         
         // Alghorithm from: https://en.wikipedia.org/wiki/SHA-1
         
-        var message = [UInt8](self.utf8)
+        var message = input
         
         var h0 = UInt32(littleEndian: 0x67452301)
         var h1 = UInt32(littleEndian: 0xEFCDAB89)
@@ -42,17 +34,17 @@ extension String {
         
         let padBytesCount = ( message.count + 8 ) % 64
         
-        message.appendContentsOf([UInt8](count: 64 - padBytesCount, repeatedValue: 0))
+        message.append(contentsOf: [UInt8](repeating: 0, count: 64 - padBytesCount))
         
         // append ml, in a 64-bit big-endian integer. Thus, the total length is a multiple of 512 bits.
         
         var mlBigEndian = ml.bigEndian
-        let bytePtr = withUnsafePointer(&mlBigEndian) { UnsafeBufferPointer<UInt8>(start: UnsafePointer($0), count: sizeofValue(mlBigEndian)) }
-        
-        message.appendContentsOf(Array(bytePtr))
+        withUnsafePointer(to: &mlBigEndian) {
+            message.append(contentsOf: Array(UnsafeBufferPointer<UInt8>(start: UnsafePointer(OpaquePointer($0)), count: 8)))
+        }
         
         // Process the message in successive 512-bit chunks ( 64 bytes chunks ):
-
+        
         for chunkStart in 0..<message.count/64 {
             var words = [UInt32]()
             let chunk = message[chunkStart*64..<chunkStart*64+64]
@@ -60,14 +52,14 @@ extension String {
             // break chunk into sixteen 32-bit big-endian words w[i], 0 ≤ i ≤ 15
             
             for i in 0...15 {
-                let value = chunk.withUnsafeBufferPointer({ UnsafePointer<UInt32>($0.baseAddress + (i*4)).memory })
+                let value = chunk.withUnsafeBufferPointer({ UnsafePointer<UInt32>(OpaquePointer($0.baseAddress! + (i*4))).pointee})
                 words.append(value.bigEndian)
             }
             
             // Extend the sixteen 32-bit words into eighty 32-bit words:
             
             for i in 16...79 {
-                let value = words[i-3] ^ words[i-8] ^ words[i-14] ^ words[i-16]
+                let value: UInt32 = ((words[i-3]) ^ (words[i-8]) ^ (words[i-14]) ^ (words[i-16]))
                 words.append(rotateLeft(value, 1))
             }
             
@@ -83,19 +75,19 @@ extension String {
                 var f = UInt32(0)
                 var k = UInt32(0)
                 switch i {
-                    case 0...19:
-                        f = (b & c) | ((~b) & d)
-                        k = 0x5A827999
-                    case 20...39:
-                        f = b ^ c ^ d
-                        k = 0x6ED9EBA1
-                    case 40...59:
-                        f = (b & c) | (b & d) | (c & d)
-                        k = 0x8F1BBCDC
-                    case 60...79:
-                        f = b ^ c ^ d
-                        k = 0xCA62C1D6
-                    default: break
+                case 0...19:
+                    f = (b & c) | ((~b) & d)
+                    k = 0x5A827999
+                case 20...39:
+                    f = b ^ c ^ d
+                    k = 0x6ED9EBA1
+                case 40...59:
+                    f = (b & c) | (b & d) | (c & d)
+                    k = 0x8F1BBCDC
+                case 60...79:
+                    f = b ^ c ^ d
+                    k = 0xCA62C1D6
+                default: break
                 }
                 let temp = (rotateLeft(a, 5) &+ f &+ e &+ k &+ words[i]) & 0xFFFFFFFF
                 e = d
@@ -116,24 +108,30 @@ extension String {
         
         // Produce the final hash value (big-endian) as a 160 bit number:
         
-        var result = [UInt8]()
+        var digest = [UInt8]()
         
-        let h0Big = h0.bigEndian
-        let h1Big = h1.bigEndian
-        let h2Big = h2.bigEndian
-        let h3Big = h3.bigEndian
-        let h4Big = h4.bigEndian
+        [h0, h1, h2, h3, h4].forEach { value in
+            var bigEndianVersion = value.bigEndian
+            withUnsafePointer(to: &bigEndianVersion) {
+                digest.append(contentsOf: Array(UnsafeBufferPointer<UInt8>(start: UnsafePointer(OpaquePointer($0)), count: 4)))
+            }
+        }
         
-        result += ([UInt8(h0Big & 0xFF), UInt8((h0Big >> 8) & 0xFF), UInt8((h0Big >> 16) & 0xFF), UInt8((h0Big >> 24) & 0xFF)]);
-        result += ([UInt8(h1Big & 0xFF), UInt8((h1Big >> 8) & 0xFF), UInt8((h1Big >> 16) & 0xFF), UInt8((h1Big >> 24) & 0xFF)]);
-        result += ([UInt8(h2Big & 0xFF), UInt8((h2Big >> 8) & 0xFF), UInt8((h2Big >> 16) & 0xFF), UInt8((h2Big >> 24) & 0xFF)]);
-        result += ([UInt8(h3Big & 0xFF), UInt8((h3Big >> 8) & 0xFF), UInt8((h3Big >> 16) & 0xFF), UInt8((h3Big >> 24) & 0xFF)]);
-        result += ([UInt8(h4Big & 0xff), UInt8((h4Big >> 8) & 0xFF), UInt8((h4Big >> 16) & 0xFF), UInt8((h4Big >> 24) & 0xFF)]);
-
-        return result;
+        return digest
     }
     
-    func rotateLeft(v: UInt32, _ n: UInt32) -> UInt32 {
+    private static func rotateLeft(_ v: UInt32, _ n: UInt32) -> UInt32 {
         return ((v << n) & 0xFFFFFFFF) | (v >> (32 - n))
     }
 }
+
+extension String {
+    
+    public func sha1() -> [UInt8] {
+        return SHA1.hash([UInt8](self.utf8))
+    }
+    
+    public func sha1() -> String {
+        return self.sha1().reduce("") { $0 + String(format: "%02x", $1) }
+    }
+}

+ 60 - 63
Sources/WebSockets.swift

@@ -5,94 +5,91 @@
 //  Copyright © 2014-2016 Damian Kołakowski. All rights reserved.
 //
 
-#if os(Linux)
-    import Glibc
-#else
-    import Foundation
-#endif
+import Foundation
+
 
 public func websocket(
-        text: ((WebSocketSession, String) -> Void)?,
-    _ binary: ((WebSocketSession, [UInt8]) -> Void)?) -> (HttpRequest -> HttpResponse) {
+      _ 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"])"))
+            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"])"))
+            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"])"))
+            return .badRequest(.text("Invalid value of 'Sec-Websocket-Key' header: \(r.headers["sec-websocket-key"])"))
         }
-        let protocolSessionClosure: (Socket -> Void) = { socket in
+        let protocolSessionClosure: ((Socket) -> Void) = { socket in
             let session = WebSocketSession(socket)
-            var fragmentedOpCode = WebSocketSession.OpCode.Close
+            var fragmentedOpCode = WebSocketSession.OpCode.close
             var payload = [UInt8]() // Used for fragmented frames.
             
-            func handleTextPayload(frame: WebSocketSession.Frame) throws {
+            func handleTextPayload(_ frame: WebSocketSession.Frame) throws {
                 if let handleText = text {
                     if frame.fin {
                         if payload.count > 0 {
-                            throw WebSocketSession.Error.ProtocolError("Continuing fragmented frame cannot have an operation code.")
+                            throw WebSocketSession.WsError.protocolError("Continuing fragmented frame cannot have an operation code.")
                         }
                         var textFramePayload = frame.payload.map { Int8(bitPattern: $0) }
                         textFramePayload.append(0)
-                        if let text = String(UTF8String: textFramePayload) {
+                        if let text = String(validatingUTF8: textFramePayload) {
                             handleText(session, text)
                         } else {
-                            throw WebSocketSession.Error.InvalidUTF8("")
+                            throw WebSocketSession.WsError.invalidUTF8("")
                         }
                     } else {
-                        payload.appendContentsOf(frame.payload)
-                        fragmentedOpCode = .Text
+                        payload.append(contentsOf: frame.payload)
+                        fragmentedOpCode = .text
                     }
                 }
             }
             
-            func handleBinaryPayload(frame: WebSocketSession.Frame) throws {
+            func handleBinaryPayload(_ frame: WebSocketSession.Frame) throws {
                 if let handleBinary = binary {
                     if frame.fin {
                         if payload.count > 0 {
-                            throw WebSocketSession.Error.ProtocolError("Continuing fragmented frame cannot have an operation code.")
+                            throw WebSocketSession.WsError.protocolError("Continuing fragmented frame cannot have an operation code.")
                         }
                         handleBinary(session, frame.payload)
                     } else {
-                        payload.appendContentsOf(frame.payload)
-                        fragmentedOpCode = .Binary
+                        payload.append(contentsOf: frame.payload)
+                        fragmentedOpCode = .binary
                     }
                 }
             }
             
-            func handleOperationCode(frame: WebSocketSession.Frame) throws {
+            func handleOperationCode(_ frame: WebSocketSession.Frame) throws {
                 switch frame.opcode {
-                case .Continue:
+                case .continue:
                     // There is no message to continue, failed immediatelly.
-                    if fragmentedOpCode == .Close {
-                        socket.shutdwn()
+                    if fragmentedOpCode == .close {
+                        socket.close()
                     }
                     frame.opcode = fragmentedOpCode
                     if frame.fin {
-                        payload.appendContentsOf(frame.payload)
+                        payload.append(contentsOf: frame.payload)
                         frame.payload = payload
                         // Clean the buffer.
                         payload = []
                         // Reset the OpCode.
-                        fragmentedOpCode = WebSocketSession.OpCode.Close
+                        fragmentedOpCode = WebSocketSession.OpCode.close
                     }
                     try handleOperationCode(frame)
-                case .Text:
+                case .text:
                     try handleTextPayload(frame)
-                case .Binary:
+                case .binary:
                     try handleBinaryPayload(frame)
-                case .Close:
-                    throw WebSocketSession.Control.Close
-                case .Ping:
+                case .close:
+                    throw WebSocketSession.Control.close
+                case .ping:
                     if frame.payload.count > 125 {
-                        throw WebSocketSession.Error.ProtocolError("Payload gretter than 125 octets.")
+                        throw WebSocketSession.WsError.protocolError("Payload gretter than 125 octets.")
                     } else {
-                        session.writeFrame(ArraySlice(frame.payload), .Pong)
+                        session.writeFrame(ArraySlice(frame.payload), .pong)
                     }
-                case .Pong:
+                case .pong:
                     break
                 }
             }
@@ -104,16 +101,16 @@ public func websocket(
                 }
             } catch let error {
                 switch error {
-                case WebSocketSession.Control.Close:
+                case WebSocketSession.Control.close:
                     // Normal close
                     break
-                case WebSocketSession.Error.UnknownOpCode:
+                case WebSocketSession.WsError.unknownOpCode:
                     print("Unknown Op Code: \(error)")
-                case WebSocketSession.Error.UnMaskedFrame:
+                case WebSocketSession.WsError.unMaskedFrame:
                     print("Unmasked frame: \(error)")
-                case WebSocketSession.Error.InvalidUTF8:
+                case WebSocketSession.WsError.invalidUTF8:
                     print("Invalid UTF8 character: \(error)")
-                case WebSocketSession.Error.ProtocolError:
+                case WebSocketSession.WsError.protocolError:
                     print("Protocol error: \(error)")
                 default:
                     print("Unkown error \(error)")
@@ -122,20 +119,20 @@ public func websocket(
                 session.writeCloseFrame()
             }
         }
-        let secWebSocketAccept = String.toBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").SHA1())
+        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)
+        return HttpResponse.switchProtocols(headers, protocolSessionClosure)
     }
 }
 
 public class WebSocketSession: Hashable, Equatable  {
     
-    public enum Error: ErrorType { case UnknownOpCode(String), UnMaskedFrame(String), ProtocolError(String), InvalidUTF8(String) }
-    public enum OpCode: UInt8 { case Continue = 0x00, Close = 0x08, Ping = 0x09, Pong = 0x0A, Text = 0x01, Binary = 0x02 }
-    public enum Control: ErrorType { case Close }
+    public enum WsError: Error { case unknownOpCode(String), unMaskedFrame(String), protocolError(String), invalidUTF8(String) }
+    public enum OpCode: UInt8 { case `continue` = 0x00, close = 0x08, ping = 0x09, pong = 0x0A, text = 0x01, binary = 0x02 }
+    public enum Control: Error { case close }
     
     public class Frame {
-        public var opcode = OpCode.Close
+        public var opcode = OpCode.close
         public var fin = false
         public var rsv1: UInt8 = 0
         public var rsv2: UInt8 = 0
@@ -143,7 +140,7 @@ public class WebSocketSession: Hashable, Equatable  {
         public var payload = [UInt8]()
     }
 
-    private let socket: Socket
+    let socket: Socket
     
     public init(_ socket: Socket) {
         self.socket = socket
@@ -151,22 +148,22 @@ public class WebSocketSession: Hashable, Equatable  {
     
     deinit {
         writeCloseFrame()
-        socket.shutdwn()
+        socket.close()
     }
     
-    public func writeText(text: String) -> Void {
-        self.writeFrame(ArraySlice(text.utf8), OpCode.Text)
+    public func writeText(_ text: String) -> Void {
+        self.writeFrame(ArraySlice(text.utf8), OpCode.text)
     }
 
-    public func writeBinary(binary: [UInt8]) -> Void {
+    public func writeBinary(_ binary: [UInt8]) -> Void {
         self.writeBinary(ArraySlice(binary))
     }
     
-    public func writeBinary(binary: ArraySlice<UInt8>) -> Void {
-        self.writeFrame(binary, OpCode.Binary)
+    public func writeBinary(_ binary: ArraySlice<UInt8>) -> Void {
+        self.writeFrame(binary, OpCode.binary)
     }
     
-    private func writeFrame(data: ArraySlice<UInt8>, _ op: OpCode, _ fin: Bool = true) {
+    public 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 {
@@ -178,11 +175,11 @@ public class WebSocketSession: Hashable, Equatable  {
         }
     }
     
-    private func writeCloseFrame() {
-        writeFrame(ArraySlice("".utf8), .Close)
+    public func writeCloseFrame() {
+        writeFrame(ArraySlice("".utf8), .close)
     }
     
-    private func encodeLengthAndMaskFlag(len: UInt64, _ masked: Bool) -> [UInt8] {
+    private func encodeLengthAndMaskFlag(_ len: UInt64, _ masked: Bool) -> [UInt8] {
         let encodedLngth = UInt8(masked ? 0x80 : 0x00)
         var encodedBytes = [UInt8]()
         switch len {
@@ -215,20 +212,20 @@ public class WebSocketSession: Hashable, Equatable  {
         frm.rsv3 = fst & 0x10
         guard frm.rsv1 == 0 && frm.rsv2 == 0 && frm.rsv3 == 0
             else {
-            throw Error.ProtocolError("Reserved frame bit has not been negocitated.")
+            throw WsError.protocolError("Reserved frame bit has not been negocitated.")
         }
         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)")
+            throw WsError.unknownOpCode("\(opc)")
         }
         if frm.fin == false {
             switch opcode {
-            case .Ping, .Pong, .Close:
+            case .ping, .pong, .close:
                 // Control frames must not be fragmented
                 // https://tools.ietf.org/html/rfc6455#section-5.5 ( Page 35 )
-                throw Error.ProtocolError("Control frames must not be framgemted.")
+                throw WsError.protocolError("Control frames must not be fragmented.")
             default:
                 break
             }
@@ -239,7 +236,7 @@ public class WebSocketSession: Hashable, Equatable  {
         guard msk else {
             // "...a client MUST mask all frames that it sends to the server."
             // http://tools.ietf.org/html/rfc6455#section-5.1
-            throw Error.UnMaskedFrame("A client must mask all frames that it sends to the server.")
+            throw WsError.unMaskedFrame("A client must mask all frames that it sends to the server.")
         }
         var len = UInt64(sec & 0x7F)
         if len == 0x7E {

+ 2 - 2
Swifter.podspec

@@ -1,7 +1,7 @@
 Pod::Spec.new do |s|
 
   s.name                  = "Swifter"
-  s.version               = "1.2.6"
+  s.version               = "1.3.2"
   s.summary               = "Tiny http server engine written in Swift programming language."
   s.homepage              = "https://github.com/glock45/swifter"
   s.license               = { :type => 'Copyright', :file => 'LICENSE' }
@@ -9,7 +9,7 @@ Pod::Spec.new do |s|
   s.ios.deployment_target = "8.0"
   s.osx.deployment_target = "10.9"
   s.tvos.deployment_target = "9.0"
-  s.source                = { :git => "https://github.com/glock45/swifter.git", :tag => "1.2.6" }
+  s.source                = { :git => "https://github.com/glock45/swifter.git", :tag => "1.3.2" }
   s.source_files          = 'Sources/*.{h,m,swift}'
 
 end

+ 61 - 34
XCode/Swifter.xcodeproj/project.pbxproj

@@ -21,7 +21,6 @@
 		269B478E1D3AAAE20042D137 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F11D2C44F30030FC98 /* HttpServer.swift */; };
 		269B478F1D3AAAE20042D137 /* HttpRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EE1D2C44F30030FC98 /* HttpRequest.swift */; };
 		269B47901D3AAAE20042D137 /* DemoServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EA1D2C44F30030FC98 /* DemoServer.swift */; };
-		269B47911D3AAAE20042D137 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EB1D2C44F30030FC98 /* File.swift */; };
 		269B47921D3AAAE20042D137 /* Socket+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B29E1D369BEC00D35BFB /* Socket+File.swift */; };
 		269B47931D3AAAE20042D137 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F51D2C44F30030FC98 /* Socket.swift */; };
 		269B47941D3AAAE20042D137 /* HttpServerIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F21D2C44F30030FC98 /* HttpServerIO.swift */; };
@@ -34,6 +33,8 @@
 		7AE893EA1C05127900A29F63 /* SwifteriOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AE893E91C05127900A29F63 /* SwifteriOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		7AE893FE1C0512C400A29F63 /* SwifterMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AE893FD1C0512C400A29F63 /* SwifterMac.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		7AE8940D1C05151100A29F63 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AE8940C1C05151100A29F63 /* Launch Screen.storyboard */; };
+		7C377E181D964B96009C6148 /* String+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C377E161D964B6A009C6148 /* String+File.swift */; };
+		7C377E191D964B9F009C6148 /* String+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C377E161D964B6A009C6148 /* String+File.swift */; };
 		7C458EFC1D4A7526006A68E5 /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */; };
 		7C458EFD1D4A7526006A68E5 /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */; };
 		7C458EFE1D4A7526006A68E5 /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */; };
@@ -47,8 +48,6 @@
 		7C76B2A31D369C9D00D35BFB /* Errno.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B2A11D369C9D00D35BFB /* Errno.swift */; };
 		7C76B70D1D2C456A0030FC98 /* DemoServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EA1D2C44F30030FC98 /* DemoServer.swift */; };
 		7C76B70E1D2C456B0030FC98 /* DemoServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EA1D2C44F30030FC98 /* DemoServer.swift */; };
-		7C76B70F1D2C456D0030FC98 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EB1D2C44F30030FC98 /* File.swift */; };
-		7C76B7101D2C456D0030FC98 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EB1D2C44F30030FC98 /* File.swift */; };
 		7C76B7111D2C45710030FC98 /* Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EC1D2C44F30030FC98 /* Files.swift */; };
 		7C76B7121D2C45710030FC98 /* Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EC1D2C44F30030FC98 /* Files.swift */; };
 		7C76B7131D2C45730030FC98 /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6ED1D2C44F30030FC98 /* HttpParser.swift */; };
@@ -79,15 +78,34 @@
 		7C76B72C1D2C45950030FC98 /* WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F91D2C44F30030FC98 /* WebSockets.swift */; };
 		7CA4813E19A2EA8D0030B30D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4813D19A2EA8D0030B30D /* main.swift */; };
 		7CB102E01A17381D00CBA3B4 /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 7CB102DF1A17381D00CBA3B4 /* logo.png */; };
+		7CCB8C5E1D97B852008B9712 /* String+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C377E161D964B6A009C6148 /* String+File.swift */; };
+		7CCB8C601D97B8CC008B9712 /* SwifterTestsHttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCB8C5F1D97B8CC008B9712 /* SwifterTestsHttpRouter.swift */; };
+		7CCB8C621D97B8E9008B9712 /* SwifterTestsHttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCB8C5F1D97B8CC008B9712 /* SwifterTestsHttpRouter.swift */; };
 		7CCD87611C66099B0068099B /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AE893E71C05127900A29F63 /* Swifter.framework */; };
 		7CCD87701C660B250068099B /* SwifterTestsHttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCD876D1C660B250068099B /* SwifterTestsHttpParser.swift */; };
 		7CCD87721C660B250068099B /* SwifterTestsStringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCD876E1C660B250068099B /* SwifterTestsStringExtensions.swift */; };
-		7CCD877E1C660EA30068099B /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AE893FB1C0512C400A29F63 /* Swifter.framework */; };
 		7CCD87841C660ED60068099B /* SwifterTestsHttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCD876D1C660B250068099B /* SwifterTestsHttpParser.swift */; };
 		7CCD87851C660ED60068099B /* SwifterTestsStringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCD876E1C660B250068099B /* SwifterTestsStringExtensions.swift */; };
 		7CDAB8131BE2A1D400C8A977 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7CDAB80D1BE2A1D400C8A977 /* Main.storyboard */; };
 		7CDAB8141BE2A1D400C8A977 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7CDAB80F1BE2A1D400C8A977 /* Images.xcassets */; };
 		7CDAB8161BE2A1D400C8A977 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CDAB8111BE2A1D400C8A977 /* ViewController.swift */; };
+		7CEBB86F1D94612D00370A6B /* Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EC1D2C44F30030FC98 /* Files.swift */; };
+		7CEBB8701D94612D00370A6B /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6ED1D2C44F30030FC98 /* HttpParser.swift */; };
+		7CEBB8711D94612D00370A6B /* HttpRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EE1D2C44F30030FC98 /* HttpRequest.swift */; };
+		7CEBB8721D94612D00370A6B /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6EF1D2C44F30030FC98 /* HttpResponse.swift */; };
+		7CEBB8731D94612D00370A6B /* HttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F01D2C44F30030FC98 /* HttpRouter.swift */; };
+		7CEBB8741D94612D00370A6B /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F11D2C44F30030FC98 /* HttpServer.swift */; };
+		7CEBB8751D94612D00370A6B /* HttpServerIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F21D2C44F30030FC98 /* HttpServerIO.swift */; };
+		7CEBB8761D94612D00370A6B /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F31D2C44F30030FC98 /* Process.swift */; };
+		7CEBB8771D94612D00370A6B /* Scopes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F41D2C44F30030FC98 /* Scopes.swift */; };
+		7CEBB8781D94612D00370A6B /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F51D2C44F30030FC98 /* Socket.swift */; };
+		7CEBB8791D94612D00370A6B /* Socket+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B29E1D369BEC00D35BFB /* Socket+File.swift */; };
+		7CEBB87A1D94612D00370A6B /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */; };
+		7CEBB87B1D94612D00370A6B /* String+BASE64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F61D2C44F30030FC98 /* String+BASE64.swift */; };
+		7CEBB87C1D94612D00370A6B /* String+Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F71D2C44F30030FC98 /* String+Misc.swift */; };
+		7CEBB87D1D94612D00370A6B /* String+SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F81D2C44F30030FC98 /* String+SHA1.swift */; };
+		7CEBB87E1D94612D00370A6B /* WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B6F91D2C44F30030FC98 /* WebSockets.swift */; };
+		7CEBB87F1D94612D00370A6B /* Errno.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C76B2A11D369C9D00D35BFB /* Errno.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -112,13 +130,6 @@
 			remoteGlobalIDString = 7AE893E61C05127900A29F63;
 			remoteInfo = SwifteriOS;
 		};
-		7CCD877F1C660EA30068099B /* PBXContainerItemProxy */ = {
-			isa = PBXContainerItemProxy;
-			containerPortal = 7C839B6619422CFF003A6950 /* Project object */;
-			proxyType = 1;
-			remoteGlobalIDString = 7AE893FA1C0512C400A29F63;
-			remoteInfo = SwifterMac;
-		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -147,12 +158,12 @@
 		7AE893FD1C0512C400A29F63 /* SwifterMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwifterMac.h; sourceTree = "<group>"; };
 		7AE893FF1C0512C400A29F63 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		7AE8940C1C05151100A29F63 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
+		7C377E161D964B6A009C6148 /* String+File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+File.swift"; sourceTree = "<group>"; };
 		7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Socket+Server.swift"; sourceTree = "<group>"; };
 		7C4785E81C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsWebSocketSession.swift; sourceTree = "<group>"; };
 		7C76B29E1D369BEC00D35BFB /* Socket+File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Socket+File.swift"; sourceTree = "<group>"; };
 		7C76B2A11D369C9D00D35BFB /* Errno.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errno.swift; sourceTree = "<group>"; };
 		7C76B6EA1D2C44F30030FC98 /* DemoServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoServer.swift; sourceTree = "<group>"; };
-		7C76B6EB1D2C44F30030FC98 /* File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
 		7C76B6EC1D2C44F30030FC98 /* Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Files.swift; sourceTree = "<group>"; };
 		7C76B6ED1D2C44F30030FC98 /* HttpParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpParser.swift; sourceTree = "<group>"; };
 		7C76B6EE1D2C44F30030FC98 /* HttpRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpRequest.swift; sourceTree = "<group>"; };
@@ -171,6 +182,7 @@
 		7CA4813B19A2EA8D0030B30D /* SwifterSampleOSX */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SwifterSampleOSX; sourceTree = BUILT_PRODUCTS_DIR; };
 		7CA4813D19A2EA8D0030B30D /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
 		7CB102DF1A17381D00CBA3B4 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = "<group>"; };
+		7CCB8C5F1D97B8CC008B9712 /* SwifterTestsHttpRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsHttpRouter.swift; sourceTree = "<group>"; };
 		7CCD875C1C66099B0068099B /* SwifteriOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwifteriOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		7CCD87601C66099B0068099B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		7CCD876D1C660B250068099B /* SwifterTestsHttpParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsHttpParser.swift; sourceTree = "<group>"; };
@@ -232,7 +244,6 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				7CCD877E1C660EA30068099B /* Swifter.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -270,7 +281,6 @@
 			isa = PBXGroup;
 			children = (
 				7C76B6EA1D2C44F30030FC98 /* DemoServer.swift */,
-				7C76B6EB1D2C44F30030FC98 /* File.swift */,
 				7C76B6EC1D2C44F30030FC98 /* Files.swift */,
 				7C76B6ED1D2C44F30030FC98 /* HttpParser.swift */,
 				7C76B6EE1D2C44F30030FC98 /* HttpRequest.swift */,
@@ -288,6 +298,7 @@
 				7C76B6F81D2C44F30030FC98 /* String+SHA1.swift */,
 				7C76B6F91D2C44F30030FC98 /* WebSockets.swift */,
 				7C76B2A11D369C9D00D35BFB /* Errno.swift */,
+				7C377E161D964B6A009C6148 /* String+File.swift */,
 			);
 			name = Sources;
 			path = ../Sources;
@@ -351,6 +362,7 @@
 		7CCD876C1C660B250068099B /* SwifterTestsCommon */ = {
 			isa = PBXGroup;
 			children = (
+				7CCB8C5F1D97B8CC008B9712 /* SwifterTestsHttpRouter.swift */,
 				7CCD876D1C660B250068099B /* SwifterTestsHttpParser.swift */,
 				0858E7F61D68BC2600491CD1 /* PingServer.swift */,
 				7CCD876E1C660B250068099B /* SwifterTestsStringExtensions.swift */,
@@ -530,7 +542,6 @@
 			buildRules = (
 			);
 			dependencies = (
-				7CCD87801C660EA30068099B /* PBXTargetDependency */,
 			);
 			name = SwifterOSXTests;
 			productName = SwifterOSXTests;
@@ -544,7 +555,7 @@
 			isa = PBXProject;
 			attributes = {
 				LastSwiftUpdateCheck = 0720;
-				LastUpgradeCheck = 0730;
+				LastUpgradeCheck = 0800;
 				ORGANIZATIONNAME = "Damian Kołakowski";
 				TargetAttributes = {
 					7AE893E61C05127900A29F63 = {
@@ -655,7 +666,6 @@
 				269B478E1D3AAAE20042D137 /* HttpServer.swift in Sources */,
 				269B478F1D3AAAE20042D137 /* HttpRequest.swift in Sources */,
 				269B47901D3AAAE20042D137 /* DemoServer.swift in Sources */,
-				269B47911D3AAAE20042D137 /* File.swift in Sources */,
 				269B47921D3AAAE20042D137 /* Socket+File.swift in Sources */,
 				269B47931D3AAAE20042D137 /* Socket.swift in Sources */,
 				269B47941D3AAAE20042D137 /* HttpServerIO.swift in Sources */,
@@ -672,6 +682,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7C377E191D964B9F009C6148 /* String+File.swift in Sources */,
 				7C76B7171D2C45780030FC98 /* HttpResponse.swift in Sources */,
 				7C76B7211D2C45870030FC98 /* Scopes.swift in Sources */,
 				7C76B71F1D2C45840030FC98 /* Process.swift in Sources */,
@@ -681,7 +692,6 @@
 				7C76B71B1D2C457E0030FC98 /* HttpServer.swift in Sources */,
 				7C76B7151D2C45760030FC98 /* HttpRequest.swift in Sources */,
 				7C76B70D1D2C456A0030FC98 /* DemoServer.swift in Sources */,
-				7C76B70F1D2C456D0030FC98 /* File.swift in Sources */,
 				7C76B29F1D369BEC00D35BFB /* Socket+File.swift in Sources */,
 				7C76B7231D2C45890030FC98 /* Socket.swift in Sources */,
 				7C76B71D1D2C45820030FC98 /* HttpServerIO.swift in Sources */,
@@ -698,6 +708,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7C377E181D964B96009C6148 /* String+File.swift in Sources */,
 				7C76B7181D2C45790030FC98 /* HttpResponse.swift in Sources */,
 				7C76B7221D2C45870030FC98 /* Scopes.swift in Sources */,
 				7C76B7201D2C45840030FC98 /* Process.swift in Sources */,
@@ -707,7 +718,6 @@
 				7C76B71C1D2C457E0030FC98 /* HttpServer.swift in Sources */,
 				7C76B7161D2C45760030FC98 /* HttpRequest.swift in Sources */,
 				7C76B70E1D2C456B0030FC98 /* DemoServer.swift in Sources */,
-				7C76B7101D2C456D0030FC98 /* File.swift in Sources */,
 				7C76B2A01D369BEC00D35BFB /* Socket+File.swift in Sources */,
 				7C76B7241D2C458A0030FC98 /* Socket.swift in Sources */,
 				7C76B71E1D2C45820030FC98 /* HttpServerIO.swift in Sources */,
@@ -727,6 +737,7 @@
 				7C73C6921C26179C00AEF6CA /* AppDelegate.swift in Sources */,
 				7CDAB8161BE2A1D400C8A977 /* ViewController.swift in Sources */,
 				0858E7F71D68BC2600491CD1 /* PingServer.swift in Sources */,
+				7CCB8C601D97B8CC008B9712 /* SwifterTestsHttpRouter.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -754,6 +765,25 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7CCB8C621D97B8E9008B9712 /* SwifterTestsHttpRouter.swift in Sources */,
+				7CCB8C5E1D97B852008B9712 /* String+File.swift in Sources */,
+				7CEBB86F1D94612D00370A6B /* Files.swift in Sources */,
+				7CEBB8701D94612D00370A6B /* HttpParser.swift in Sources */,
+				7CEBB8711D94612D00370A6B /* HttpRequest.swift in Sources */,
+				7CEBB8721D94612D00370A6B /* HttpResponse.swift in Sources */,
+				7CEBB8731D94612D00370A6B /* HttpRouter.swift in Sources */,
+				7CEBB8741D94612D00370A6B /* HttpServer.swift in Sources */,
+				7CEBB8751D94612D00370A6B /* HttpServerIO.swift in Sources */,
+				7CEBB8761D94612D00370A6B /* Process.swift in Sources */,
+				7CEBB8771D94612D00370A6B /* Scopes.swift in Sources */,
+				7CEBB8781D94612D00370A6B /* Socket.swift in Sources */,
+				7CEBB8791D94612D00370A6B /* Socket+File.swift in Sources */,
+				7CEBB87A1D94612D00370A6B /* Socket+Server.swift in Sources */,
+				7CEBB87B1D94612D00370A6B /* String+BASE64.swift in Sources */,
+				7CEBB87C1D94612D00370A6B /* String+Misc.swift in Sources */,
+				7CEBB87D1D94612D00370A6B /* String+SHA1.swift in Sources */,
+				7CEBB87E1D94612D00370A6B /* WebSockets.swift in Sources */,
+				7CEBB87F1D94612D00370A6B /* Errno.swift in Sources */,
 				7CCD87841C660ED60068099B /* SwifterTestsHttpParser.swift in Sources */,
 				0858E7F91D68BC2600491CD1 /* PingServer.swift in Sources */,
 				0858E7F51D68BB2600491CD1 /* IOSafetyTests.swift in Sources */,
@@ -780,11 +810,6 @@
 			target = 7AE893E61C05127900A29F63 /* SwifteriOS */;
 			targetProxy = 7CCD87621C66099B0068099B /* PBXContainerItemProxy */;
 		};
-		7CCD87801C660EA30068099B /* PBXTargetDependency */ = {
-			isa = PBXTargetDependency;
-			target = 7AE893FA1C0512C400A29F63 /* SwifterMac */;
-			targetProxy = 7CCD877F1C660EA30068099B /* PBXContainerItemProxy */;
-		};
 /* End PBXTargetDependency section */
 
 /* Begin PBXVariantGroup section */
@@ -803,7 +828,7 @@
 		269B479F1D3AAAE20042D137 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				CURRENT_PROJECT_VERSION = 1.2.5;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEFINES_MODULE = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1;
@@ -831,7 +856,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1.2.5;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEFINES_MODULE = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1;
@@ -858,7 +883,7 @@
 		7AE893ED1C05127900A29F63 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				CURRENT_PROJECT_VERSION = 1.2.5;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEFINES_MODULE = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1;
@@ -885,7 +910,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1.2.5;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEFINES_MODULE = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1;
@@ -913,7 +938,7 @@
 			buildSettings = {
 				CODE_SIGN_IDENTITY = "Mac Developer";
 				COMBINE_HIDPI_IMAGES = YES;
-				CURRENT_PROJECT_VERSION = 1.2.5;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEFINES_MODULE = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1;
@@ -942,7 +967,7 @@
 				CODE_SIGN_IDENTITY = "Mac Developer";
 				COMBINE_HIDPI_IMAGES = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1.2.5;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEFINES_MODULE = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1;
@@ -985,7 +1010,7 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1.2.6;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
@@ -1009,6 +1034,7 @@
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 3.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Debug;
@@ -1033,7 +1059,7 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				COPY_PHASE_STRIP = YES;
-				CURRENT_PROJECT_VERSION = 1.2.6;
+				CURRENT_PROJECT_VERSION = 1.3.2;
 				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -1049,6 +1075,7 @@
 				METAL_ENABLE_DEBUG_INFO = NO;
 				ONLY_ACTIVE_ARCH = NO;
 				SDKROOT = macosx;
+				SWIFT_VERSION = 3.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALIDATE_PRODUCT = YES;
 			};
@@ -1164,7 +1191,7 @@
 				MTL_ENABLE_DEBUG_INFO = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = pl.kolakowski.SwifterOSXTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = appletvos;
+				SDKROOT = macosx;
 			};
 			name = Debug;
 		};
@@ -1182,7 +1209,7 @@
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_BUNDLE_IDENTIFIER = pl.kolakowski.SwifterOSXTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = appletvos;
+				SDKROOT = macosx;
 			};
 			name = Release;
 		};

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifterMac.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0730"
+   LastUpgradeVersion = "0800"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifteriOS.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0730"
+   LastUpgradeVersion = "0800"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwiftertvOS.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0730"
+   LastUpgradeVersion = "0800"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/SwifterMac/Info.plist

@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.2.6</string>
+	<string>1.3.2</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>

+ 9 - 5
XCode/SwifterSampleOSX/main.swift

@@ -8,17 +8,21 @@ import Foundation
 import Swifter
 
 do {
-    let server = demoServer(try File.currentWorkingDirectory())
+    let server = demoServer(try String.File.currentWorkingDirectory())
     server["/testAfterBaseRoute"] = { request in
-        return .OK(.Html("ok !"))
+        return .ok(.html("ok !"))
     }
     
-    try server.start(9080, forceIPv4: true)
+    if #available(OSXApplicationExtension 10.10, *) {
+        try server.start(9080, forceIPv4: true)
+    } else {
+        // Fallback on earlier versions
+    }
     
     print("Server has started ( port = \(try server.port()) ). Try to connect now...")
     
-    NSRunLoop.mainRunLoop().run()
+    RunLoop.main.run()
     
 } catch {
     print("Server start error: \(error)")
-}
+}

+ 2 - 3
XCode/SwifterSampleiOS/AppDelegate.swift

@@ -12,8 +12,7 @@ import Swifter
 class AppDelegate: UIResponder, UIApplicationDelegate {
     
     var window: UIWindow?
-    
-    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
         return true
     }
-}
+}

+ 1 - 1
XCode/SwifterSampleiOS/Info.plist

@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.2.6</string>
+	<string>1.3.2</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>

+ 1 - 1
XCode/SwifterSampleiOS/ViewController.swift

@@ -14,7 +14,7 @@ class ViewController: UIViewController {
     override func viewDidLoad() {
         super.viewDidLoad()
         do {
-            let server = demoServer(NSBundle.mainBundle().resourcePath!)
+            let server = demoServer(Bundle.main.resourcePath!)
             try server.start(9080)
             self.server = server
         } catch {

+ 11 - 8
XCode/SwifterTestsCommon/IOSafetyTests.swift

@@ -7,7 +7,6 @@
 //
 
 import XCTest
-import Swifter
 
 class IOSafetyTests: XCTestCase {
     var server: HttpServer!
@@ -25,16 +24,20 @@ class IOSafetyTests: XCTestCase {
     }
 
     func testStopWithActiveConnections() {
-        (0...100).forEach { _ in
+        (0...100).forEach { cpt in
             server = HttpServer.pingServer()
-            try! server.start()
-            XCTAssertFalse(NSURLSession.sharedSession().retryPing())
-            (0...100).forEach { _ in
-                dispatch_async(dispatch_get_global_queue(0, 0)) {
-                    NSURLSession.sharedSession().pingTask { _, _, _ in }.resume()
+            do {
+                try server.start()
+                XCTAssertFalse(URLSession.shared.retryPing())
+                (0...100).forEach { _ in
+                    DispatchQueue.global(qos: DispatchQoS.default.qosClass).sync {
+                        URLSession.shared.pingTask { _, _, _ in }.resume()
+                    }
                 }
+                server.stop()
+            } catch let e {
+                XCTFail("\(cpt): \(e)")
             }
-            server.stop()
         }
     }
 }

+ 19 - 20
XCode/SwifterTestsCommon/PingServer.swift

@@ -7,55 +7,54 @@
 //
 
 import Foundation
-import Swifter
 
 // Server
 extension HttpServer {
     class func pingServer() -> HttpServer {
         let server = HttpServer()
         server.GET["/ping"] = { request in
-            return HttpResponse.OK(.Text("pong!"))
+            return HttpResponse.ok(.text("pong!"))
         }
         return server
     }
 }
 
-let defaultLocalhost = NSURL(string:"http://localhost:8080")!
+let defaultLocalhost = URL(string:"http://localhost:8080")!
 
 // Client
-extension NSURLSession {
+extension URLSession {
     func pingTask(
-        hostURL: NSURL = defaultLocalhost,
-        completionHandler handler: (NSData?, NSURLResponse?, NSError?) -> Void
-    ) -> NSURLSessionDataTask {
-        return self.dataTaskWithURL(hostURL.URLByAppendingPathComponent("/ping"), completionHandler: handler)
+        hostURL: URL = defaultLocalhost,
+        completionHandler handler: @escaping (Data?, URLResponse?, Error?) -> Void
+    ) -> URLSessionDataTask {
+        return self.dataTask(with: hostURL.appendingPathComponent("/ping"), completionHandler: handler)
     }
     
     func retryPing(
-        hostURL: NSURL = defaultLocalhost,
+        hostURL: URL = defaultLocalhost,
         timeout: Double = 2.0
     ) -> Bool {
-        let semaphore = dispatch_semaphore_create(0)
+        let semaphore = DispatchSemaphore(value: 0)
         self.signalIfPongReceived(semaphore, hostURL: hostURL)
-        let timeoutDate = NSDate().dateByAddingTimeInterval(timeout)
+        let timeoutDate = NSDate().addingTimeInterval(timeout)
         var timedOut = false
-        while dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW) != 0 {
-            if NSDate().laterDate(timeoutDate) != timeoutDate {
+        while semaphore.wait(timeout: DispatchTime.now()) != DispatchTimeoutResult.timedOut {
+            if NSDate().laterDate(timeoutDate as Date) != timeoutDate as Date {
                 timedOut = true
                 break
             }
-            NSRunLoop.currentRunLoop().runMode(
-                NSRunLoopCommonModes,
-                beforeDate: NSDate.distantFuture()
+            RunLoop.current.run(
+                mode: RunLoopMode.commonModes,
+                before: NSDate.distantFuture
             )
         }
         return timedOut
     }
     
-    func signalIfPongReceived(semaphore: dispatch_semaphore_t, hostURL: NSURL) {
-        pingTask(hostURL) { data, response, error in
-            if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 {
-                dispatch_semaphore_signal(semaphore)
+    func signalIfPongReceived(_ semaphore: DispatchSemaphore, hostURL: URL) {
+        pingTask(hostURL: hostURL) { data, response, error in
+            if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
+                semaphore.signal()
             } else {
                 self.signalIfPongReceived(semaphore, hostURL: hostURL)
             }

+ 25 - 26
XCode/SwifterTestsCommon/SwifterTestsHttpParser.swift

@@ -6,7 +6,6 @@
 //
 
 import XCTest
-import Swifter
 
 class SwifterTestsHttpParser: XCTestCase {
     
@@ -16,16 +15,16 @@ class SwifterTestsHttpParser: XCTestCase {
         
         init(_ content: String) {
             super.init(socketFileDescriptor: -1)
-            self.content.appendContentsOf([UInt8](content.utf8))
+            self.content.append(contentsOf: [UInt8](content.utf8))
         }
-
+        
         override func read() throws -> UInt8 {
             if offset < content.count {
                 let value = self.content[offset]
                 offset = offset + 1
                 return value
             }
-            throw SocketError.RecvFailed("")
+            throw SocketError.recvFailed("")
         }
     }
     
@@ -33,73 +32,73 @@ class SwifterTestsHttpParser: XCTestCase {
         let parser = HttpParser()
         
         do {
-            try parser.readHttpRequest(TestSocket(""))
+            let _ = try parser.readHttpRequest(TestSocket(""))
             XCTAssert(false, "Parser should throw an error if socket is empty.")
         } catch { }
-
+        
         do {
-            try parser.readHttpRequest(TestSocket("12345678"))
+            let _ = try parser.readHttpRequest(TestSocket("12345678"))
             XCTAssert(false, "Parser should throw an error if status line has single token.")
         } catch { }
-
+        
         do {
-            try parser.readHttpRequest(TestSocket("GET HTTP/1.0"))
+            let _ = try parser.readHttpRequest(TestSocket("GET HTTP/1.0"))
             XCTAssert(false, "Parser should throw an error if status line has not enough tokens.")
         } catch { }
-
+        
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0"))
             XCTAssert(false, "Parser should throw an error if there is no next line symbol.")
         } catch { }
-            
+        
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0"))
             XCTAssert(false, "Parser should throw an error if there is no next line symbol.")
         } catch { }
         
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\r"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\r"))
             XCTAssert(false, "Parser should throw an error if there is no next line symbol.")
         } catch { }
         
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\n"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\n"))
             XCTAssert(false, "Parser should throw an error if there is no 'Content-Length' header.")
         } catch { }
         
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\r\nContent-Length: 0\r\n\r\n"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\r\nContent-Length: 0\r\n\r\n"))
         } catch {
             XCTAssert(false, "Parser should not throw any errors if there is a valid 'Content-Length' header.")
         }
         
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 0\r\n\n"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 0\r\n\n"))
         } catch {
             XCTAssert(false, "Parser should not throw any errors if there is a valid 'Content-Length' header.")
         }
         
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 5\n\n12345"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 5\n\n12345"))
         } catch {
             XCTAssert(false, "Parser should not throw any errors if there is a valid 'Content-Length' header.")
         }
         
         do {
-            try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 10\r\n\n"))
+            let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 10\r\n\n"))
             XCTAssert(false, "Parser should throw an error if request' body is too short.")
         } catch { }
         
         var r = try? parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 10\n\n1234567890"))
-        XCTAssert(r?.method == "GET", "Parser should extract HTTP method name from the status line.")
-        XCTAssert(r?.path == "/", "Parser should extract HTTP path value from the status line.")
-        XCTAssert(r?.headers["content-length"] == "10", "Parser should extract Content-Length header value.")
+        XCTAssertEqual(r?.method, "GET", "Parser should extract HTTP method name from the status line.")
+        XCTAssertEqual(r?.path, "/", "Parser should extract HTTP path value from the status line.")
+        XCTAssertEqual(r?.headers["content-length"], "10", "Parser should extract Content-Length header value.")
         
         r = try? parser.readHttpRequest(TestSocket("POST / HTTP/1.0\nContent-Length: 10\n\n1234567890"))
-        XCTAssert(r?.method == "POST", "Parser should extract HTTP method name from the status line.")
+        XCTAssertEqual(r?.method, "POST", "Parser should extract HTTP method name from the status line.")
         
         r = try? parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nHeader1: 1\nHeader2: 2\nContent-Length: 0\n\n"))
-        XCTAssert(r?.headers["header1"] == "1", "Parser should extract multiple headers from the request.")
-        XCTAssert(r?.headers["header2"] == "2", "Parser should extract multiple headers from the request.")
+        XCTAssertEqual(r?.headers["header1"], "1", "Parser should extract multiple headers from the request.")
+        XCTAssertEqual(r?.headers["header2"], "2", "Parser should extract multiple headers from the request.")
     }
-}
+}

+ 108 - 0
XCode/SwifterTestsCommon/SwifterTestsHttpRouter.swift

@@ -0,0 +1,108 @@
+//
+//  SwifterTestsHttpRouter.swift
+//  Swifter
+//
+
+//  Copyright © 2016 Damian Kołakowski. All rights reserved.
+//
+
+import XCTest
+
+class SwifterTestsHttpRouter: XCTestCase {
+    
+    func testHttpRouterSlashRoot() {
+        
+        let router = HttpRouter()
+        
+        router.register(nil, path: "/", handler: { r in
+            return .ok(.html("OK"))
+        })
+        
+        XCTAssertNotNil(router.route(nil, path: "/"))
+    }
+    
+    func testHttpRouterSimplePathSegments() {
+        
+        let router = HttpRouter()
+        
+        router.register(nil, path: "/a/b/c/d", handler: { r in
+            return .ok(.html("OK"))
+        })
+        
+        XCTAssertNil(router.route(nil, path: "/"))
+        XCTAssertNil(router.route(nil, path: "/a"))
+        XCTAssertNil(router.route(nil, path: "/a/b"))
+        XCTAssertNil(router.route(nil, path: "/a/b/c"))
+        XCTAssertNotNil(router.route(nil, path: "/a/b/c/d"))
+    }
+    
+    func testHttpRouterSinglePathSegmentWildcard() {
+        
+        let router = HttpRouter()
+        
+        router.register(nil, path: "/a/*/c/d", handler: { r in
+            return .ok(.html("OK"))
+        })
+        
+        XCTAssertNil(router.route(nil, path: "/"))
+        XCTAssertNil(router.route(nil, path: "/a"))
+        XCTAssertNotNil(router.route(nil, path: "/a/foo/c/d"))
+        XCTAssertNotNil(router.route(nil, path: "/a/b/c/d"))
+        XCTAssertNil(router.route(nil, path: "/a/b"))
+        XCTAssertNil(router.route(nil, path: "/a/b/foo/d"))
+    }
+    
+    func testHttpRouterVariables() {
+        
+        let router = HttpRouter()
+        
+        router.register(nil, path: "/a/:arg1/:arg2/b/c/d/:arg3", handler: { r in
+            return .ok(.html("OK"))
+        })
+        
+        XCTAssertNil(router.route(nil, path: "/"))
+        XCTAssertNil(router.route(nil, path: "/a"))
+        XCTAssertNil(router.route(nil, path: "/a/b/c/d"))
+        XCTAssertEqual(router.route(nil, path: "/a/value1/value2/b/c/d/value3")?.0[":arg1"], "value1")
+        XCTAssertEqual(router.route(nil, path: "/a/value1/value2/b/c/d/value3")?.0[":arg2"], "value2")
+        XCTAssertEqual(router.route(nil, path: "/a/value1/value2/b/c/d/value3")?.0[":arg3"], "value3")
+    }
+    
+    func testHttpRouterMultiplePathSegmentWildcards() {
+        
+        let router = HttpRouter()
+        
+        router.register(nil, path: "/a/**/e/f/g", handler: { r in
+            return .ok(.html("OK"))
+        })
+        
+        XCTAssertNil(router.route(nil, path: "/"))
+        XCTAssertNil(router.route(nil, path: "/a"))
+        XCTAssertNotNil(router.route(nil, path: "/a/b/c/d/e/f/g"))
+        XCTAssertNil(router.route(nil, path: "/a/e/f/g"))
+    }
+    
+    func testHttpRouterEmptyTail() {
+        
+        let router = HttpRouter()
+        
+        router.register(nil, path: "/a/b/", handler: { r in
+            return .ok(.html("OK"))
+        })
+        
+        router.register(nil, path: "/a/b/:var", handler: { r in
+            return .ok(.html("OK"))
+        })
+        
+        
+        XCTAssertNil(router.route(nil, path: "/"))
+        XCTAssertNil(router.route(nil, path: "/a"))
+        XCTAssertNotNil(router.route(nil, path: "/a/b/"))
+        XCTAssertNil(router.route(nil, path: "/a/e/f/g"))
+        
+        XCTAssertEqual(router.route(nil, path: "/a/b/value1")?.0[":var"], "value1")
+        
+        XCTAssertEqual(router.route(nil, path: "/a/b/")?.0[":var"], "")
+    }
+    
+}

+ 10 - 11
XCode/SwifterTestsCommon/SwifterTestsStringExtensions.swift

@@ -6,19 +6,18 @@
 //
 
 import XCTest
-import Swifter
 
 class SwifterTestsStringExtensions: XCTestCase {
     
     func testSHA1() {
-        XCTAssertEqual("".SHA1(), "da39a3ee5e6b4b0d3255bfef95601890afd80709")
-        XCTAssertEqual("test".SHA1(), "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3")
+        XCTAssertEqual("".sha1(), "da39a3ee5e6b4b0d3255bfef95601890afd80709")
+        XCTAssertEqual("test".sha1(), "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3")
         
         // Values copied from OpenSSL:
         // https://github.com/openssl/openssl/blob/master/test/sha1test.c
         
-        XCTAssertEqual("abc".SHA1(), "a9993e364706816aba3e25717850c26c9cd0d89d")
-        XCTAssertEqual("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".SHA1(),
+        XCTAssertEqual("abc".sha1(), "a9993e364706816aba3e25717850c26c9cd0d89d")
+        XCTAssertEqual("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".sha1(),
             "84983e441c3bd26ebaae4aa1f95129e5e54670f1")
         
         XCTAssertEqual(
@@ -34,7 +33,7 @@ class SwifterTestsStringExtensions: XCTestCase {
              "a9993e364706816aba3e25717850c26c9cd0d89d" +
              "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +
              "a9993e364706816aba3e25717850c26c9cd0d89d" +
-             "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq").SHA1(),
+             "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq").sha1(),
             "a377b0c42d685fdc396e29a9eda7101d900947ca")
     }
     
@@ -82,11 +81,11 @@ class SwifterTestsStringExtensions: XCTestCase {
     }
 
     func testMiscReplace() {
-        XCTAssertEqual("".replace("+", "-"), "")
-        XCTAssertEqual("test".replace("+", "-"), "test")
-        XCTAssertEqual("+++".replace("+", "-"), "---")
-        XCTAssertEqual("t&e&s&t12%3%".replace("&", "+").replace("%", "+"), "t+e+s+t12+3+")
-        XCTAssertEqual("test 1234 #$%^&*( test   ".replace(" ", "_"), "test_1234_#$%^&*(_test___")
+        XCTAssertEqual("".replace(old: "+", "-"), "")
+        XCTAssertEqual("test".replace(old: "+", "-"), "test")
+        XCTAssertEqual("+++".replace(old: "+", "-"), "---")
+        XCTAssertEqual("t&e&s&t12%3%".replace(old: "&", "+").replace(old: "%", "+"), "t+e+s+t12+3+")
+        XCTAssertEqual("test 1234 #$%^&*( test   ".replace(old: " ", "_"), "test_1234_#$%^&*(_test___")
     }
     
     func testMiscRemovePercentEncoding() {

+ 19 - 20
XCode/SwifterTestsCommon/SwifterTestsWebSocketSession.swift

@@ -6,7 +6,6 @@
 //
 
 import XCTest
-import Swifter
 
 class SwifterTestsWebSocketSession: XCTestCase {
     
@@ -16,7 +15,7 @@ class SwifterTestsWebSocketSession: XCTestCase {
         
         init(_ content: [UInt8]) {
             super.init(socketFileDescriptor: -1)
-            self.content.appendContentsOf(content)
+            self.content.append(contentsOf: content)
         }
         
         override func read() throws -> UInt8 {
@@ -25,7 +24,7 @@ class SwifterTestsWebSocketSession: XCTestCase {
                 offset = offset + 1
                 return value
             }
-            throw SocketError.RecvFailed("")
+            throw SocketError.recvFailed("")
         }
     }
     
@@ -33,7 +32,7 @@ class SwifterTestsWebSocketSession: XCTestCase {
         
         do {
             let session = WebSocketSession(TestSocket([0]))
-            try session.readFrame()
+            let _ = try session.readFrame()
             XCTAssert(false, "Parser should throw an error if socket has not enough data for a frame.")
         } catch {
             XCTAssert(true, "Parser should throw an error if socket has not enough data for a frame.")
@@ -41,9 +40,9 @@ class SwifterTestsWebSocketSession: XCTestCase {
         
         do {
             let session = WebSocketSession(TestSocket([0b0000_0001, 0b0000_0000, 0, 0, 0, 0]))
-            try session.readFrame()
+            let _ = try session.readFrame()
             XCTAssert(false, "Parser should not accept unmasked frames.")
-        } catch WebSocketSession.Error.UnMaskedFrame {
+        } catch WebSocketSession.WsError.unMaskedFrame {
             XCTAssert(true, "Parse should throw UnMaskedFrame error for unmasked message.")
         } catch {
             XCTAssert(false, "Parse should throw UnMaskedFrame error for unmasked message.")
@@ -60,7 +59,7 @@ class SwifterTestsWebSocketSession: XCTestCase {
         do {
             let session = WebSocketSession(TestSocket([0b0000_0000, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, 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.")
         }
@@ -68,7 +67,7 @@ class SwifterTestsWebSocketSession: XCTestCase {
         do {
             let session = WebSocketSession(TestSocket([0b0000_0001, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, 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.")
         }
@@ -76,7 +75,7 @@ class SwifterTestsWebSocketSession: XCTestCase {
         do {
             let session = WebSocketSession(TestSocket([0b0000_0010, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, 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.")
         }
@@ -84,33 +83,33 @@ class SwifterTestsWebSocketSession: XCTestCase {
         do {
             let session = WebSocketSession(TestSocket([0b1000_1000, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Close, "Parser should accept Close opcode.")
-        } catch {
-            XCTAssert(false, "Parser should accept Close opcode without any errors: \(error).")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.close, "Parser should accept Close opcode.")
+        } catch let e {
+            XCTAssert(false, "Parser should accept Close opcode without any errors. \(e)")
         }
         
         do {
             let session = WebSocketSession(TestSocket([0b1000_1001, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Ping, "Parser should accept Ping opcode.")
-        } catch {
-            XCTAssert(false, "Parser should accept Ping opcode without any errors: \(error).")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.ping, "Parser should accept Ping opcode.")
+        } catch let e {
+            XCTAssert(false, "Parser should accept Ping opcode without any errors. \(e)")
         }
         
         do {
             let session = WebSocketSession(TestSocket([0b1000_1010, 0b1000_0000, 0, 0, 0, 0]))
             let frame = try session.readFrame()
-            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.Pong, "Parser should accept Pong opcode.")
-        } catch {
-            XCTAssert(false, "Parser should accept Pong opcode without any errors: \(error).")
+            XCTAssertEqual(frame.opcode, WebSocketSession.OpCode.pong, "Parser should accept Pong opcode.")
+        } catch let e {
+            XCTAssert(false, "Parser should accept Pong opcode without any errors. \(e)")
         }
         
         for opcode in [3, 4, 5, 6, 7, 11, 12, 13, 14, 15] {
             do {
                 let session = WebSocketSession(TestSocket([UInt8(opcode), 0b1000_0000, 0, 0, 0, 0]))
-                try session.readFrame()
+                let _ = try session.readFrame()
                 XCTAssert(false, "Parse should throw an error for unknown opcode: \(opcode)")
-            } catch WebSocketSession.Error.UnknownOpCode(_) {
+            } catch WebSocketSession.WsError.unknownOpCode(_) {
                 XCTAssert(true, "Parse should throw UnknownOpCode error for unknown opcode.")
             } catch {
                 XCTAssert(false, "Parse should throw UnknownOpCode error for unknown opcode (was \(error)).")

+ 1 - 1
XCode/SwifteriOS/Info.plist

@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.2.6</string>
+	<string>1.3.2</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>

+ 1 - 1
XCode/SwiftertvOS/Info.plist

@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.2.6</string>
+	<string>1.3.2</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>