Procházet zdrojové kódy

Ready for Swift 3.0 (XCode 8).

Damian Kołakowski před 9 roky
rodič
revize
6abd986c30

+ 3 - 3
Sources/DemoServer.swift

@@ -11,7 +11,7 @@
     import Foundation
 #endif
 
-public func demoServer(publicDir: String) -> HttpServer {
+public func demoServer(_ publicDir: String) -> HttpServer {
     
     print(publicDir)
     
@@ -139,7 +139,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 {
@@ -181,7 +181,7 @@ 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)
     })
     

+ 1 - 2
Sources/Errno.swift

@@ -2,7 +2,6 @@
 //  Errno.swift
 //  Swifter
 //
-//  Created by Damian Kolakowski on 13/07/16.
 //  Copyright © 2016 Damian Kołakowski. All rights reserved.
 //
 
@@ -15,6 +14,6 @@
 public class Errno {
     
     public class func description() -> String {
-        return String.fromCString(UnsafePointer(strerror(errno))) ?? "Error: \(errno)"
+        return String(cString: UnsafePointer(strerror(errno)))
     }
 }

+ 23 - 29
Sources/File.swift

@@ -11,7 +11,7 @@
     import Foundation
 #endif
 
-public enum FileError: ErrorType {
+public enum FileError: Error {
     case OpenFailed(String)
     case WriteFailed(String)
     case ReadFailed(String)
@@ -23,27 +23,26 @@ public enum FileError: ErrorType {
 
 public class File {
     
-    public static func openNewForWriting(path: String) throws -> File {
+    public static func openNewForWriting(_ path: String) throws -> File {
         return try openFileForMode(path, "wb")
     }
     
-    public static func openForReading(path: String) throws -> File {
+    public static func openForReading(_ path: String) throws -> File {
         return try openFileForMode(path, "rb")
     }
     
-    public static func openForWritingAndReading(path: String) throws -> File {
+    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 {
+    public static func openFileForMode(_ path: String, _ mode: String) throws -> File {
+        guard let file = path.withCString({ pathPointer in mode.withCString({ fopen(pathPointer, $0) }) }) else {
             throw FileError.OpenFailed(Errno.description())
         }
         return File(file)
     }
     
-    public static func isDirectory(path: String) throws -> Bool {
+    public static func isDirectory(_ path: String) throws -> Bool {
         var s = stat()
         guard path.withCString({ stat($0, &s) }) == 0 else {
             throw FileError.IsDirectoryFailed(Errno.description())
@@ -52,37 +51,32 @@ public class File {
     }
     
     public static func currentWorkingDirectory() throws -> String {
-        let path = getcwd(nil, 0)
-        if path == nil {
+        guard let path = getcwd(nil, 0) else {
             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
+        return String(cString: path)
     }
     
-    public static func exists(path: String) throws -> Bool {
+    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 {
+    public static func list(_ path: String) throws -> [String] {
+        guard let dir = path.withCString({ opendir($0) }) else {
             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
+        while let ent = readdir(dir) {
+            var name = ent.pointee.d_name
+            let fileName = withUnsafePointer(to: &name) { (ptr) -> String? in
                 #if os(Linux)
-                    return String.fromCString([CChar](UnsafeBufferPointer<CChar>(start: UnsafePointer(unsafeBitCast(ptr, UnsafePointer<CChar>.self)), count: Int(NAME_MAX))))
+                    return String.fromCString([CChar](UnsafeBufferPointer<CChar>(start: UnsafePointer(unsafeBitCast(ptr, UnsafePointer<CChar>.self)), count: 256)))
                 #else
-                    var buffer = [CChar](UnsafeBufferPointer(start: unsafeBitCast(ptr, UnsafePointer<CChar>.self), count: Int(ent.memory.d_namlen)))
+                    var buffer = [CChar](UnsafeBufferPointer(start: unsafeBitCast(ptr, to: UnsafePointer<CChar>.self), count: Int(ent.pointee.d_namlen)))
                     buffer.append(0)
-                    return String.fromCString(buffer)
+                    return String(validatingUTF8: buffer)
                 #endif
             }
             if let fileName = fileName {
@@ -102,7 +96,7 @@ public class File {
         fclose(pointer)
     }
     
-    public func read(inout data: [UInt8]) throws -> Int {
+    public func read( data: inout [UInt8]) throws -> Int {
         if data.count <= 0 {
             return data.count
         }
@@ -138,19 +132,19 @@ public class File {
 
 }
 
-public func withNewFileOpenedForWriting<Result>(path: String, _ f: File throws -> Result) throws -> Result {
+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 {
+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 {
+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 {
+public func withFileOpenedForMode<Result>(_ path: String, mode: String, _ f: (File) throws -> Result) throws -> Result {
     let file = try File.openFileForMode(path, mode)
     defer {
         file.close()

+ 2 - 2
Sources/Files.swift

@@ -11,7 +11,7 @@
     import Foundation
 #endif
 
-public func shareFilesFromDirectory(directoryPath: String) -> (HttpRequest -> HttpResponse) {
+public func shareFilesFromDirectory(_ directoryPath: String) -> ((HttpRequest) -> HttpResponse) {
     return { r in
         guard let fileRelativePath = r.params.first else {
             return .NotFound
@@ -27,7 +27,7 @@ public func shareFilesFromDirectory(directoryPath: String) -> (HttpRequest -> Ht
     }
 }
 
-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

+ 10 - 10
Sources/HttpParser.swift

@@ -11,7 +11,7 @@
     import Foundation
 #endif
 
-enum HttpParserError: ErrorType {
+enum HttpParserError: Error {
     case InvalidStatusLine(String)
 }
 
@@ -19,7 +19,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 +36,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 {

+ 17 - 17
Sources/HttpResponse.swift

@@ -11,16 +11,17 @@
     import Foundation
 #endif
 
-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: 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 {
@@ -30,7 +31,7 @@ public enum HttpResponseBody {
     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):
@@ -40,11 +41,10 @@ public enum HttpResponseBody {
                         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)
                     })
@@ -78,12 +78,12 @@ public enum HttpResponseBody {
 
 public enum HttpResponse {
     
-    case SwitchProtocols([String: String], Socket -> 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)? )
+    case RAW(Int, String, [String:String]?, ((HttpResponseBodyWriter) throws -> Void)? )
 
     func statusCode() -> Int {
         switch self {
@@ -143,7 +143,7 @@ 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)
@@ -152,9 +152,9 @@ public enum HttpResponse {
         }
     }
     
-    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
         }
     }

+ 35 - 20
Sources/HttpRouter.swift

@@ -15,7 +15,7 @@ public class HttpRouter {
     
     private class Node {
         var nodes = [String: Node]()
-        var handler: (HttpRequest -> HttpResponse)? = nil
+        var handler: ((HttpRequest) -> HttpResponse)? = nil
     }
     
     private var rootNode = Node()
@@ -23,44 +23,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 +68,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 +80,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 +96,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
@@ -100,16 +107,24 @@ public class HttpRouter {
             params[variableNode.0] = pathToken
             return findHandler(&node.nodes[variableNode.0]!, params: &params, generator: &generator)
         }
-        if var node = node.nodes[pathToken] {
-            return findHandler(&node, params: &params, generator: &generator)
+        if let _ = node.nodes[pathToken] {
+            return findHandler(&node.nodes[pathToken]!, params: &params, generator: &generator)
+        }
+        if let _ = node.nodes["*"] {
+            return findHandler(&node.nodes["*"]!, params: &params, generator: &generator)
         }
-        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
         }

+ 4 - 4
Sources/HttpServer.swift

@@ -36,7 +36,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 +47,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 +69,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)
             }

+ 22 - 17
Sources/HttpServerIO.swift

@@ -25,7 +25,7 @@ public class HttpServerIO {
         }
     }
     public var operating: Bool { get { return self.state == .Running } }
-    private let queue = dispatch_queue_create("swifter.httpserverio.clientsockets", DISPATCH_QUEUE_SERIAL)
+    private let queue = DispatchQueue(label: "swifter.httpserverio.clientsockets")
     
     public func port() throws -> Int {
         return Int(try socket.port())
@@ -39,26 +39,27 @@ public class HttpServerIO {
         stop()
     }
     
-    public func start(port: in_port_t = 8080, forceIPv4: Bool = false, priority: Int = DISPATCH_QUEUE_PRIORITY_BACKGROUND) throws {
+    @available(OSXApplicationExtension 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.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()
         }
@@ -72,18 +73,18 @@ public class HttpServerIO {
         for socket in self.sockets {
             socket.shutdwn()
         }
-        dispatch_sync(queue) {
-            self.sockets.removeAll(keepCapacity: true)
+        self.queue.sync {
+            self.sockets.removeAll(keepingCapacity: true)
         }
         socket.release()
         self.state = .Stopped
     }
     
-    public func dispatch(request: HttpRequest) -> ([String: String], HttpRequest -> HttpResponse) {
+    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
@@ -113,24 +114,28 @@ public class HttpServerIO {
         
         let socket: Socket
 
-        func write(file: File) throws {
+        func write(_ file: 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")

+ 3 - 3
Sources/Process.swift

@@ -13,11 +13,11 @@
 
 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 +30,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) {

+ 162 - 160
Sources/Scopes.swift

@@ -11,11 +11,13 @@
     import Foundation
 #endif
 
-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 +148,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 +180,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 +253,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 +263,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 +587,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 +727,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 +740,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.

+ 2 - 2
Sources/Socket+File.swift

@@ -38,11 +38,11 @@
 
 extension Socket {
     
-    public func writeFile(file: File) throws -> Void {
+    public func writeFile(_ file: File) throws -> Void {
         var offset: off_t = 0
         let result = sendfileImpl(fileno(file.pointer), self.socketFileDescriptor, 0, &offset, nil, 0)
         if result == -1 {
-            throw SocketError.WriteFailed("sendfile: " + Errno.description())
+            throw SocketError.writeFailed("sendfile: " + Errno.description())
         }
     }
     

+ 24 - 14
Sources/Socket+Server.swift

@@ -13,7 +13,7 @@
 
 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,14 +22,14 @@ 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)
+            throw SocketError.socketSettingReUseAddrFailed(details)
         }
         Socket.setNoSigPipe(socketFileDescriptor)
         
@@ -58,25 +58,25 @@ extension Socket {
         #else
             var bindResult: Int32 = -1
             if forceIPv4 {
-                var addr = sockaddr_in(sin_len: UInt8(strideof(sockaddr_in)),
+                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))
                 
-                bindResult = withUnsafePointer(&addr) {
-                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in)))
+                bindResult = withUnsafePointer(to: &addr) {
+                    bind(socketFileDescriptor, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in>.size))
                 }
             } else {
-                var addr = sockaddr_in6(sin6_len: UInt8(strideof(sockaddr_in6)),
+                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)
                 
-                bindResult = withUnsafePointer(&addr) {
-                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6)))
+                bindResult = withUnsafePointer(to: &addr) {
+                    bind(socketFileDescriptor, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in6>.size))
                 }
             }
         #endif
@@ -84,15 +84,25 @@ extension Socket {
         if bindResult == -1 {
             let details = Errno.description()
             Socket.release(socketFileDescriptor)
-            throw SocketError.BindFailed(details)
+            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)
+            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)
+    }
 }

+ 51 - 55
Sources/Socket.swift

@@ -13,18 +13,18 @@
 
 /* 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 {
@@ -59,23 +59,12 @@ public class Socket: Hashable, Equatable {
         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())
-        }
-        Socket.setNoSigPipe(clientSocket)
-        return Socket(socketFileDescriptor: clientSocket)
-    }
-    
     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)
@@ -87,34 +76,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)
@@ -123,17 +118,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)
+        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]
     }
@@ -152,46 +147,47 @@ 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())
+            throw SocketError.getNameInfoFailed(Errno.description())
         }
-        guard let name = String.fromCString(hostBuffer) else {
-            throw SocketError.ConvertingPeerNameFailed
-        }
-        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)))
+            setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &no_sig_pipe, socklen_t(MemoryLayout<Int32>.size))
         #endif
     }
     
-    public class func shutdwn(socket: Int32) {
+    public class func shutdwn(_ socket: Int32) {
         #if os(Linux)
             shutdown(socket, Int32(SHUT_RDWR))
         #else
-            Darwin.shutdown(socket, SHUT_RDWR)
+            let _ = Darwin.shutdown(socket, SHUT_RDWR)
         #endif
     }
     
-    public class func release(socket: Int32) {
+    public class func release(_ socket: Int32) {
         #if os(Linux)
             shutdown(socket, Int32(SHUT_RDWR))
+            close(socket)
         #else
-            Darwin.shutdown(socket, SHUT_RDWR)
+            if Darwin.shutdown(socket, SHUT_RDWR) != -1 {
+                // If you close socket which was already closed it produces exception visible in TestFlight's crash log.
+                // This is easily can be fixed by checking result on shutdown function != -1.
+                close(socket)
+            }
         #endif
-        close(socket)
     }
 }
 

+ 4 - 4
Sources/String+BASE64.swift

@@ -16,13 +16,13 @@ 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 +38,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)

+ 9 - 9
Sources/String+Misc.swift

@@ -13,12 +13,12 @@
 
 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 +44,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 +57,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 +69,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

+ 43 - 41
Sources/String+SHA1.swift

@@ -12,17 +12,13 @@
 #endif
 
 
-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 +38,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 +56,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 +79,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 +112,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) }
+    }
+}

+ 32 - 32
Sources/WebSockets.swift

@@ -12,8 +12,8 @@
 #endif
 
 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"])"))
@@ -24,46 +24,46 @@ public func websocket(
         guard let secWebSocketKey = r.headers["sec-websocket-key"] else {
             return .BadRequest(.Text("Invalid value of 'Sec-Websocket-Key' header: \(r.headers["sec-websocket-key"])"))
         }
-        let protocolSessionClosure: (Socket -> Void) = { socket in
+        let protocolSessionClosure: ((Socket) -> Void) = { socket in
             let session = WebSocketSession(socket)
             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)
+                        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)
+                        payload.append(contentsOf: frame.payload)
                         fragmentedOpCode = .Binary
                     }
                 }
             }
             
-            func handleOperationCode(frame: WebSocketSession.Frame) throws {
+            func handleOperationCode(_ frame: WebSocketSession.Frame) throws {
                 switch frame.opcode {
                 case .Continue:
                     // There is no message to continue, failed immediatelly.
@@ -72,7 +72,7 @@ public func websocket(
                     }
                     frame.opcode = fragmentedOpCode
                     if frame.fin {
-                        payload.appendContentsOf(frame.payload)
+                        payload.append(contentsOf: frame.payload)
                         frame.payload = payload
                         // Clean the buffer.
                         payload = []
@@ -88,7 +88,7 @@ public func websocket(
                     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)
                     }
@@ -107,13 +107,13 @@ public func websocket(
                 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,7 +122,7 @@ 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)
     }
@@ -130,9 +130,9 @@ public func websocket(
 
 public class WebSocketSession: Hashable, Equatable  {
     
-    public enum Error: ErrorType { case UnknownOpCode(String), UnMaskedFrame(String), ProtocolError(String), InvalidUTF8(String) }
+    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: ErrorType { case Close }
+    public enum Control: Error { case Close }
     
     public class Frame {
         public var opcode = OpCode.Close
@@ -143,7 +143,7 @@ public class WebSocketSession: Hashable, Equatable  {
         public var payload = [UInt8]()
     }
 
-    private let socket: Socket
+    let socket: Socket
     
     public init(_ socket: Socket) {
         self.socket = socket
@@ -154,19 +154,19 @@ public class WebSocketSession: Hashable, Equatable  {
         socket.shutdwn()
     }
     
-    public func writeText(text: String) -> Void {
+    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 {
+    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 +178,11 @@ public class WebSocketSession: Hashable, Equatable  {
         }
     }
     
-    private func writeCloseFrame() {
+    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 +215,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:
                 // 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 framgemted.")
             default:
                 break
             }
@@ -239,7 +239,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
XCode/Swifter.xcodeproj/project.pbxproj

@@ -1003,7 +1003,7 @@
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 2.3;
+				SWIFT_VERSION = 3.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Debug;
@@ -1044,7 +1044,7 @@
 				METAL_ENABLE_DEBUG_INFO = NO;
 				ONLY_ACTIVE_ARCH = NO;
 				SDKROOT = macosx;
-				SWIFT_VERSION = 2.3;
+				SWIFT_VERSION = 3.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALIDATE_PRODUCT = YES;
 			};

+ 7 - 3
XCode/SwifterSampleOSX/main.swift

@@ -13,12 +13,16 @@ do {
         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)")
-}
+}