Parcourir la source

Merge remote-tracking branch 'glock45/master'

nonn il y a 10 ans
Parent
commit
452430030e

+ 4 - 11
Common/DemoServer.swift

@@ -1,7 +1,7 @@
 //
 //  DemoServer.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation
@@ -58,13 +58,9 @@ func demoServer(publicDir: String?) -> HttpServer {
                         return .NotFound
                     }
                 }
-                break;
             case "POST":
-                if let body = request.body {
-                    return .OK(.HTML(body))
-                } else {
-                    return .OK(.HTML("No POST params."))
-            }
+                let formFields = request.parseForm()
+                return HttpResponse.OK(.HTML(formFields.map({ "\($0.0) = \($0.1)" }).joinWithSeparator("<br>")))
             default:
                 return .NotFound
         }
@@ -75,10 +71,7 @@ func demoServer(publicDir: String?) -> HttpServer {
     }
     server["/"] = { request in
         var listPage = "Available services:<br><ul>"
-        for item in server.routes {
-            listPage += "<li><a href=\"\(item)\">\(item)</a></li>"
-        }
-        listPage += "</ul>"
+        listPage += server.routes.map({ "<li><a href=\"\($0)\">\($0)</a></li>"}).joinWithSeparator("")
         return .OK(.HTML(listPage))
     }
     return server

+ 1 - 1
Common/HttpHandlers.swift

@@ -1,7 +1,7 @@
 //
 //  Handlers.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation

+ 39 - 88
Common/HttpParser.swift

@@ -1,103 +1,52 @@
 //
 //  HttpParser.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation
 
 
 enum HttpParserError : ErrorType {
-    case RecvFailed
-    case ReadBodyFailed
+    case RecvFailed(String?)
+    case ReadBodyFailed(String?)
     case InvalidStatusLine(String)
-    
-    var value: String {
-        switch self {
-            case .RecvFailed:
-                return "recv(...) failed."
-            
-            case .ReadBodyFailed:
-                return "IO error while reading body"
-            
-            case .InvalidStatusLine(let statusLine):
-                return "Invalid status line: \(statusLine)"
-        }
-    }
 }
 
 class HttpParser {
     
     func nextHttpRequest(socket: CInt) throws -> HttpRequest {
-        do {
-            let statusLine = try nextLine(socket)
-            let statusTokens = statusLine.componentsSeparatedByString(" ")
-            print(statusTokens)
-            if statusTokens.count < 3 {
-                throw HttpParserError.InvalidStatusLine(statusLine)
-            }
-            let method = statusTokens[0]
-            let path = statusTokens[1]
-            let urlParams = self.extractUrlParams(path)
-            
-            let headers = try nextHeaders(socket)
-            // TODO detect content-type and handle:
-            // 'application/x-www-form-urlencoded' -> Dictionary
-            // 'multipart' -> Dictionary
-            let body: String?
-            if let contentLengthString = headers["content-length"],
-                let contentLength = Int(contentLengthString) {
-                    body = try nextBody(socket, size: contentLength)
-                    
-//                    self.extractBodyParams(body!)
-            } else {
-                body = nil
-            }
-            
+        let statusLine = try nextLine(socket)
+        let statusLineTokens = statusLine.componentsSeparatedByString(" ")
+        print(statusLineTokens)
+        if statusLineTokens.count < 3 {
+            throw HttpParserError.InvalidStatusLine(statusLine)
+        }
+        let method = statusLineTokens[0]
+        let path = statusLineTokens[1]
+        let urlParams = extractUrlParams(path)
+        let headers = try nextHeaders(socket)
+        if let contentLength = headers["content-length"], let contentLengthValue = Int(contentLength) {
+            let body = try nextBody(socket, size: contentLengthValue)
             return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: body, capturedUrlGroups: [], address: nil)
-        } catch {
-            throw error
         }
+        return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: nil, capturedUrlGroups: [], address: nil)
     }
     
-    private func extractBodyParams(body: String) -> [String: Parameter] {
-        let params = body.componentsSeparatedByString("&").map { (param:String) -> (String, String) in
+    private func extractUrlParams(url: String) -> [(String, String)] {
+        guard let query = url.componentsSeparatedByString("?").last else {
+            return []
+        }
+        return query.componentsSeparatedByString("&").map { (param:String) -> (String, String) in
             let tokens = param.componentsSeparatedByString("=")
-            if tokens.count >= 2 {
-                let key = tokens[0].stringByRemovingPercentEncoding
-                let value = tokens[1].stringByRemovingPercentEncoding
-                if key != nil && value != nil { return (key!, value!) }
+            guard tokens.count >= 2 else {
+                return ("", "")
             }
-            return ("","")
-        }
-        
-        var result = [String: Parameter]()
-        
-        for (key, value) in params {
-            if let val = result[key] {
-                switch val {
-                case .StringValue(let stringValue):
-                    result[key] = Parameter.ArrayString([stringValue, value])
-                    
-                case .ArrayString(var arrayValue):
-                    arrayValue.append(value)
-                    result[key] = Parameter.ArrayString(arrayValue)
-                }
-            } else {
-                result[key] = Parameter.StringValue(value)
+            guard let k = tokens[0].stringByRemovingPercentEncoding, v = tokens[1].stringByRemovingPercentEncoding else {
+                return ("", "")
             }
+            return (k, v)
         }
-        
-        return result
-
-    }
-    
-    private func extractUrlParams(url: String) -> [String : Parameter] {
-        guard let query = url.componentsSeparatedByString("?").last else {
-            return [:]
-        }
-        
-        return self.extractBodyParams(query)
     }
     
     private func nextBody(socket: CInt, size: Int) throws -> String {
@@ -106,7 +55,7 @@ class HttpParser {
         while counter < size {
             let c = nextInt8(socket)
             if c < 0 {
-                throw HttpParserError.ReadBodyFailed
+                throw HttpParserError.ReadBodyFailed(String.fromCString(UnsafePointer(strerror(errno))))
             }
             body.append(UnicodeScalar(c))
             counter++;
@@ -115,30 +64,32 @@ class HttpParser {
     }
     
     private func nextHeaders(socket: CInt) throws -> [String: String] {
-        var headers = [String: String]()
-        while let headerLine = try? nextLine(socket) {
+        var requestHeaders = [String: String]()
+        repeat {
+            let headerLine = try nextLine(socket)
             if headerLine.isEmpty {
-                return headers
+                return requestHeaders
             }
             let headerTokens = headerLine.componentsSeparatedByString(":")
             if headerTokens.count >= 2 {
                 // RFC 2616 - "Hypertext Transfer Protocol -- HTTP/1.1", paragraph 4.2, "Message Headers":
                 // "Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive."
-                // We can keep lower case version.
+                // We will keep lower case version.
                 let headerName = headerTokens[0].lowercaseString
                 let headerValue = headerTokens[1].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
                 if !headerName.isEmpty && !headerValue.isEmpty {
-                    headers.updateValue(headerValue, forKey: headerName)
+                    requestHeaders.updateValue(headerValue, forKey: headerName)
                 }
             }
-        }
-        throw HttpParserError.RecvFailed
+        } while true
     }
 
     private func nextInt8(socket: CInt) -> Int {
         var buffer = [UInt8](count: 1, repeatedValue: 0);
         let next = recv(socket as Int32, &buffer, Int(buffer.count), 0)
-        if next <= 0 { return next }
+        if next <= 0 {
+            return next
+        }
         return Int(buffer[0])
     }
     
@@ -149,8 +100,8 @@ class HttpParser {
             n = nextInt8(socket)
             if ( n > 13 /* CR */ ) { characters.append(Character(UnicodeScalar(n))) }
         } while n > 0 && n != 10 /* NL */
-        if n == -1 && characters.isEmpty {
-            throw HttpParserError.RecvFailed
+        if n == -1 {
+            throw HttpParserError.RecvFailed(String.fromCString(UnsafePointer(strerror(errno))))
         }
         return characters
     }

+ 18 - 7
Common/HttpRequest.swift

@@ -1,22 +1,33 @@
 //
 //  HttpRequest.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation
 
 public struct HttpRequest {
+    
     public let url: String
-    public let urlParams: [String: Parameter] // http://stackoverflow.com/questions/1746507/authoritative-position-of-duplicate-http-get-query-keys
+    public let urlParams: [(String, String)]
     public let method: String
     public let headers: [String: String]
 	public let body: String?
     public var capturedUrlGroups: [String]
     public var address: String?
-}
-
-public enum Parameter {
-    case StringValue(String)
-    case ArrayString([String])
+    
+    public func parseForm() -> [(String, String)] {
+        if let body = body {
+            return body.componentsSeparatedByString("&").map { (param:String) -> (String, String) in
+                let tokens = param.componentsSeparatedByString("=")
+                if tokens.count >= 2 {
+                    let key = tokens[0].stringByReplacingOccurrencesOfString("+", withString: " ").stringByRemovingPercentEncoding
+                    let value = tokens[1].stringByReplacingOccurrencesOfString("+", withString: " ").stringByRemovingPercentEncoding
+                    if let key = key, value = value { return (key, value) }
+                }
+                return ("","")
+            }
+        }
+        return []
+    }
 }

+ 9 - 8
Common/HttpResponse.swift

@@ -1,7 +1,7 @@
 //
 //  HttpResponse.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation
@@ -51,7 +51,7 @@ public enum HttpResponseBody {
             return body
             
         case .HTML(let body):
-            return "<html><body>\(body)</body></html>"
+            return "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
         }
     }
 }
@@ -101,12 +101,13 @@ public enum HttpResponse {
 		case .OK(let body):
             switch body {
                 case .JSON(_)   : headers["Content-Type"] = "application/json"
-                case .PLIST(_), .XML(_)  : headers["Content-Type"] = "application/xml"
-                // 'application/xml' vs 'text/xml'
-                // From RFC: http://www.rfc-editor.org/rfc/rfc3023.txt - "If an XML document -- that is, the unprocessed, source XML document -- is readable by casual users,
-                // text/xml is preferable to application/xml. MIME user agents (and web user agents) that do not have explicit 
-                // support for text/xml will treat it as text/plain, for example, by displaying the XML MIME entity as plain text. 
-                // Application/xml is preferable when the XML MIME entity is unreadable by casual users."
+                case .PLIST(_)  : headers["Content-Type"] = "application/xml"
+                case .XML(_)    : headers["Content-Type"] = "application/xml"
+                // 'application/xml' or 'text/xml' ?
+                // From RFC: http://www.rfc-editor.org/rfc/rfc3023.txt - "If an XML document -- that is, the unprocessed, 
+                // source XML document -- is readable by casual users, text/xml is preferable to application/xml. 
+                // MIME user agents (and web user agents) that do not have explicit support for text/xml will treat it as text/plain, 
+                // for example, by displaying the XML MIME entity as plain text.
                 case .HTML(_)   : headers["Content-Type"] = "text/html"
                 default:break
             }

+ 64 - 72
Common/HttpServer.swift

@@ -1,7 +1,7 @@
 //
 //  HttpServer.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation
@@ -13,9 +13,10 @@ public class HttpServer
     public typealias Handler = HttpRequest -> HttpResponse
     
     private(set) var handlers: [(expression: NSRegularExpression, handler: Handler)] = []
-    private(set) var clientSockets: Set<CInt> = []
-    let clientSocketsLock = 0
     private(set) var acceptSocket: CInt = -1
+    private(set) var clientSockets: Set<CInt> = []
+    private let clientSocketsLock = 0
+
     
     let matchingOptions = NSMatchingOptions(rawValue: 0)
     let expressionOptions = NSRegularExpressionOptions(rawValue: 0)
@@ -26,11 +27,14 @@ public class HttpServer
         get {
             return nil
         }
-        
-        set {
-            if let newHandler = newValue,
-               let regex = try? NSRegularExpression(pattern: path, options: expressionOptions){
-                self.handlers.append(expression: regex, handler: newHandler)
+        set ( newValue ) {
+            do {
+                let regex = try NSRegularExpression(pattern: path, options: expressionOptions)
+                if let newHandler = newValue {
+                    handlers.append(expression: regex, handler: newHandler)
+                }
+            } catch  {
+                print("Could not register handler for: \(path), error: \(error)")
             }
         }
     }
@@ -41,52 +45,49 @@ public class HttpServer
     
     public func start(listenPort: in_port_t = 8080) throws {
         self.stop()
-        do {
-            let socket = try Socket.tcpForListen(listenPort)
-            self.acceptSocket = socket
-            
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
-                while let socket = try? Socket.acceptClientSocket(self.acceptSocket) {
-                    HttpServer.lock(self.clientSocketsLock) {
-                        self.clientSockets.insert(socket)
-                    }
-                    if self.acceptSocket == -1 { return }
-                    let socketAddress = try? Socket.peername(socket)!
-                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
-                        let parser = HttpParser()
-                        while let request = try? parser.nextHttpRequest(socket) {
-                            let keepAlive = parser.supportsKeepAlive(request.headers)
-                            let response: HttpResponse
-                            if let (expression, handler) = self.findHandler(request.url) {
-                                let capturedUrlsGroups = self.captureExpressionGroups(expression, value: request.url)
-                                let updatedRequest = HttpRequest(url: request.url, urlParams: request.urlParams, method: request.method, headers: request.headers, body: request.body, capturedUrlGroups: capturedUrlsGroups, address: socketAddress)
-                                response = handler(updatedRequest)
-                            } else {
-                                response = HttpResponse.NotFound
-                            }
-                            HttpServer.respond(socket, response: response, keepAlive: keepAlive)
-
-                            if !keepAlive { break }
+        self.acceptSocket = try Socket.tcpForListen(listenPort)
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
+            while let socket = try? Socket.acceptClientSocket(self.acceptSocket) {
+                HttpServer.lock(self.clientSocketsLock) {
+                    self.clientSockets.insert(socket)
+                }
+                if self.acceptSocket == -1 { return }
+                let socketAddress = try? Socket.peername(socket)!
+                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
+                    let parser = HttpParser()
+                    while let request = try? parser.nextHttpRequest(socket) {
+                        let keepAlive = parser.supportsKeepAlive(request.headers)
+                        let response: HttpResponse
+                        if let (expression, handler) = self.findHandler(request.url) {
+                            let capturedUrlsGroups = self.captureExpressionGroups(expression, value: request.url)
+                            let updatedRequest = HttpRequest(url: request.url, urlParams: request.urlParams, method: request.method, headers: request.headers, body: request.body, capturedUrlGroups: capturedUrlsGroups, address: socketAddress)
+                            response = handler(updatedRequest)
+                        } else {
+                            response = HttpResponse.NotFound
                         }
-                        Socket.release(socket)
-                        HttpServer.lock(self.clientSocketsLock) {
-                            self.clientSockets.remove(socket)
+                        do {
+                            try HttpServer.respond(socket, response: response, keepAlive: keepAlive)
+                        } catch {
+                            print("Failed to send response: \(error)")
+                            break
                         }
+                        if !keepAlive { break }
+                    }
+                    Socket.release(socket)
+                    HttpServer.lock(self.clientSocketsLock) {
+                        self.clientSockets.remove(socket)
                     }
                 }
-                self.stop()
             }
-        } catch {
-            throw error
+            self.stop()
         }
     }
     
     func findHandler(url:String) -> (NSRegularExpression, Handler)? {
-        if let u = NSURL(string: url),
-                  path = u.path {
+        if let u = NSURL(string: url), path = u.path {
             for handler in self.handlers {
                 if handler.expression.numberOfMatchesInString(path, options: self.matchingOptions, range: HttpServer.asciiRange(path)) > 0 {
-                    return handler;
+                    return handler
                 }
             }
         }
@@ -94,13 +95,11 @@ public class HttpServer
     }
     
     func captureExpressionGroups(expression: NSRegularExpression, value: String) -> [String] {
-        var capturedGroups = [String]()
-        
-        guard let u = NSURL(string: value),
-                  path = u.path else {
-                return capturedGroups
+        guard let u = NSURL(string: value), path = u.path else {
+            return []
         }
         
+        var capturedGroups = [String]()
         if let result = expression.firstMatchInString(path, options: matchingOptions, range: HttpServer.asciiRange(path)) {
             let nsValue: NSString = path
             for i in 1..<result.numberOfRanges {
@@ -108,7 +107,6 @@ public class HttpServer
                     capturedGroups.append(group)
                 }
             }
-//            for var i = 1 ; i < result.numberOfRanges ; ++i {
         }
         return capturedGroups
     }
@@ -124,38 +122,32 @@ public class HttpServer
         }
     }
     
-    public class func asciiRange(value: String) -> NSRange {
+    private class func asciiRange(value: String) -> NSRange {
         return NSMakeRange(0, value.lengthOfBytesUsingEncoding(NSASCIIStringEncoding))
     }
     
-    public class func lock(handle: AnyObject, closure: () -> ()) {
+    private class func lock(handle: AnyObject, closure: () -> ()) {
         objc_sync_enter(handle)
         closure()
         objc_sync_exit(handle)
     }
     
-    public class func respond(socket: CInt, response: HttpResponse, keepAlive: Bool) {
-        do {
-            try Socket.writeUTF8(socket, string: "HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
-            
-            let length = response.body()?.length ?? 0
-            try Socket.writeASCII(socket, string: "Content-Length: \(length)\r\n")
-            
-            if keepAlive {
-                try Socket.writeASCII(socket, string: "Connection: keep-alive\r\n")
-            }
-            for (name, value) in response.headers() {
-                try Socket.writeASCII(socket, string: "\(name): \(value)\r\n")
-            }
-            try Socket.writeASCII(socket, string: "\r\n")
-            if let body = response.body() {
-                try Socket.writeData(socket, data: body)
-            }
-        } catch {
-            // TODO: handle error
-            print("error responding to client")
-        }
+    private class func respond(socket: CInt, response: HttpResponse, keepAlive: Bool) throws {
+        try Socket.writeUTF8(socket, string: "HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
+        
+        let length = response.body()?.length ?? 0
+        try Socket.writeASCII(socket, string: "Content-Length: \(length)\r\n")
         
+        if keepAlive {
+            try Socket.writeASCII(socket, string: "Connection: keep-alive\r\n")
+        }
+        for (name, value) in response.headers() {
+            try Socket.writeASCII(socket, string: "\(name): \(value)\r\n")
+        }
+        try Socket.writeASCII(socket, string: "\r\n")
+        if let body = response.body() {
+            try Socket.writeData(socket, data: body)
+        }
     }
 }
 

+ 32 - 57
Common/Socket.swift

@@ -1,7 +1,7 @@
 //
 //  Socket.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation
@@ -9,95 +9,70 @@ import Foundation
 /* Low level routines for POSIX sockets */
 
 enum SocketError: ErrorType {
-    case SocketInitializationFailed
-    case SocketOptionInitializationFailed
-    case BindFailed
-    case ListenFailed
-    case WriteFailed
-    case PeerNameFailed
-    case NameInfoFailed
-    case AcceptFailed
-    
-    var message: String {
-        switch self {
-            case .SocketInitializationFailed:
-                return "socket(...) failed"
-            
-            case .SocketOptionInitializationFailed:
-                return "setsockopt(...) failed."
-            
-            case .BindFailed:
-                return "bind(...) failed."
-            
-            case .ListenFailed:
-                return "listen(...) failed."
-            
-            case .WriteFailed:
-                return "write(...) failed"
-            
-            case .PeerNameFailed:
-                return "getpeername(...) failed"
-            
-            case .NameInfoFailed:
-                return "getnameinfo(...) failed"
-            
-            case .AcceptFailed:
-                return "accept(...) failed."
-        }
-    }
+    case SocketInitializationFailed(String?)
+    case SocketOptionInitializationFailed(String?)
+    case BindFailed(String?)
+    case ListenFailed(String?)
+    case WriteFailed(String?)
+    case GetPeerNameFailed(String?)
+    case GetNameInfoFailed(String?)
+    case AcceptFailed(String?)
 }
 
 let maxPendingConnection: Int32 = 20
 
 struct Socket {
+    
     static func tcpForListen(port: in_port_t = 8080) throws -> CInt {
         let s = socket(AF_INET, SOCK_STREAM, 0)
         if s == -1 {
-            throw SocketError.SocketInitializationFailed
+            throw SocketError.SocketInitializationFailed(String.fromCString(UnsafePointer(strerror(errno))))
         }
-        var value: Int32 = 1
         
+        var value: Int32 = 1
         if setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32))) == -1 {
+            let details = String.fromCString(UnsafePointer(strerror(errno)))
             release(s)
-            throw SocketError.SocketOptionInitializationFailed
+            throw SocketError.SocketOptionInitializationFailed(details)
         }
+        
         nosigpipe(s)
+        
         var addr = sockaddr_in(sin_len: __uint8_t(sizeof(sockaddr_in)),
             sin_family: sa_family_t(AF_INET),
             sin_port: port_htons(port),
             sin_addr: in_addr(s_addr: inet_addr("0.0.0.0")),
             sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
-        
         var sock_addr = sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
         memcpy(&sock_addr, &addr, Int(sizeof(sockaddr_in)))
+        
         if bind(s, &sock_addr, socklen_t(sizeof(sockaddr_in))) == -1 {
+            let details = String.fromCString(UnsafePointer(strerror(errno)))
             release(s)
-            throw SocketError.BindFailed
+            throw SocketError.BindFailed(details)
         }
+        
         if listen(s, maxPendingConnection ) == -1 {
+            let details = String.fromCString(UnsafePointer(strerror(errno)))
             release(s)
-            throw SocketError.ListenFailed
+            throw SocketError.ListenFailed(details)
         }
         return s
     }
     
     static func writeUTF8(socket: CInt, string: String) throws {
         if let nsdata = string.dataUsingEncoding(NSUTF8StringEncoding) {
-            do {
-                try writeData(socket, data: nsdata)
-            } catch {
-                throw error
-            }
+            try writeData(socket, data: nsdata)
+        } else {
+            throw SocketError.WriteFailed("dataUsingEncoding(NSUTF8StringEncoding) failed")
         }
     }
     
     static func writeASCII(socket: CInt, string: String) throws {
         if let nsdata = string.dataUsingEncoding(NSASCIIStringEncoding) {
-            do {
-                try writeData(socket, data: nsdata)
-            } catch {
-                throw error
-            }
+            try writeData(socket, data: nsdata)
+        } else {
+            throw SocketError.WriteFailed("dataUsingEncoding(NSASCIIStringEncoding) failed")
         }
     }
     
@@ -107,7 +82,7 @@ struct Socket {
         while sent < data.length {
             let s = write(socket, unsafePointer + sent, Int(data.length - sent))
             if s <= 0 {
-                throw SocketError.WriteFailed
+                throw SocketError.WriteFailed(String.fromCString(UnsafePointer(strerror(errno))))
             }
             sent += s
         }
@@ -118,7 +93,7 @@ struct Socket {
         var len: socklen_t = 0
         let clientSocket = accept(socket, &addr, &len)
         if clientSocket == -1 {
-            throw SocketError.AcceptFailed
+            throw SocketError.AcceptFailed(String.fromCString(UnsafePointer(strerror(errno))))
         }
         Socket.nosigpipe(clientSocket)
         return clientSocket
@@ -143,11 +118,11 @@ struct Socket {
     static func peername(socket: CInt) throws -> String? {
         var addr = sockaddr(), len: socklen_t = socklen_t(sizeof(sockaddr))
         if getpeername(socket, &addr, &len) != 0 {
-            throw SocketError.PeerNameFailed
+            throw SocketError.GetPeerNameFailed(String.fromCString(UnsafePointer(strerror(errno))))
         }
         var hostBuffer = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
         if getnameinfo(&addr, len, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST) != 0 {
-            throw SocketError.NameInfoFailed
+            throw SocketError.GetNameInfoFailed(String.fromCString(UnsafePointer(strerror(errno))))
         }
         return String.fromCString(hostBuffer)
     }

+ 3 - 1
Common/TestSocket.swift

@@ -7,14 +7,16 @@
 //
 
 import Foundation
+
 //http://socket.io/get-started/chat/
+
 func testSocket(publicDir: String) -> HttpServer {
     let server = HttpServer()
 
     server["/resources/(.+)"] = HttpHandlers.directory(publicDir)
     
     server["/"] = { request in
-        if let html = NSData(contentsOfFile:"\(publicDir)/index.html") {
+        if let html = NSData(contentsOfFile:"\(publicDir)/chat.html") {
             return HttpResponse.RAW(200, "OK", nil, html)
         } else {
             return .NotFound

+ 0 - 0
Resources/index.html → Resources/chat.html


+ 115 - 8
Swifter.xcodeproj/project.pbxproj

@@ -8,9 +8,6 @@
 
 /* Begin PBXBuildFile section */
 		18447C3F1BD14CF50011C707 /* TestSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18447C3E1BD14CF50011C707 /* TestSocket.swift */; settings = {ASSET_TAGS = (); }; };
-		18447C411BD14E400011C707 /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 18447C401BD14E400011C707 /* index.html */; settings = {ASSET_TAGS = (); }; };
-		187F2CB71BD1501700AC8474 /* TestSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18447C3E1BD14CF50011C707 /* TestSocket.swift */; settings = {ASSET_TAGS = (); }; };
-		187F2CB81BD1511500AC8474 /* index.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 18447C401BD14E400011C707 /* index.html */; };
 		7C71C5B01A1D52F800682BF0 /* login.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 98630C061A1C9A9D00478D08 /* login.html */; };
 		7C71C5B11A1EC49B00682BF0 /* logo.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7CB102DF1A17381D00CBA3B4 /* logo.png */; };
 		7C839B7419422CFF003A6950 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C839B7319422CFF003A6950 /* AppDelegate.swift */; };
@@ -35,6 +32,19 @@
 		7CB102DD1A167FFA00CBA3B4 /* DemoServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CB102DC1A167FFA00CBA3B4 /* DemoServer.swift */; };
 		7CB102DE1A1680EA00CBA3B4 /* DemoServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CB102DC1A167FFA00CBA3B4 /* DemoServer.swift */; };
 		7CB102E01A17381D00CBA3B4 /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 7CB102DF1A17381D00CBA3B4 /* logo.png */; };
+		7CEA69AA1BD270F10009F783 /* chat.html in Resources */ = {isa = PBXBuildFile; fileRef = 7CEA69A91BD270F10009F783 /* chat.html */; settings = {ASSET_TAGS = (); }; };
+		7CEA69AF1BD271920009F783 /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4814B19A2EED00030B30D /* HttpResponse.swift */; };
+		7CEA69B11BD271920009F783 /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4814A19A2EED00030B30D /* HttpParser.swift */; };
+		7CEA69B21BD271920009F783 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4814D19A2EED00030B30D /* Socket.swift */; };
+		7CEA69B31BD271920009F783 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4814C19A2EED00030B30D /* HttpServer.swift */; };
+		7CEA69B41BD271920009F783 /* chat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEA69AB1BD271160009F783 /* chat.swift */; settings = {ASSET_TAGS = (); }; };
+		7CEA69B61BD271920009F783 /* TestSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18447C3E1BD14CF50011C707 /* TestSocket.swift */; settings = {ASSET_TAGS = (); }; };
+		7CEA69B71BD271920009F783 /* HttpHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CB102D91A1664B200CBA3B4 /* HttpHandlers.swift */; };
+		7CEA69B81BD271920009F783 /* HttpRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4815A19A2F6A60030B30D /* HttpRequest.swift */; };
+		7CEA69BB1BD271920009F783 /* logo.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7CB102DF1A17381D00CBA3B4 /* logo.png */; };
+		7CEA69BC1BD271920009F783 /* login.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 98630C061A1C9A9D00478D08 /* login.html */; };
+		7CEA69BD1BD271920009F783 /* test.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7CA4815719A2EF2B0030B30D /* test.json */; };
+		7CEA69C21BD271B20009F783 /* chat.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7CEA69A91BD270F10009F783 /* chat.html */; };
 		98630C071A1C9A9D00478D08 /* login.html in Resources */ = {isa = PBXBuildFile; fileRef = 98630C061A1C9A9D00478D08 /* login.html */; };
 /* End PBXBuildFile section */
 
@@ -46,17 +56,28 @@
 			dstSubfolderSpec = 7;
 			files = (
 				7C71C5B11A1EC49B00682BF0 /* logo.png in CopyFiles */,
-				187F2CB81BD1511500AC8474 /* index.html in CopyFiles */,
 				7C71C5B01A1D52F800682BF0 /* login.html in CopyFiles */,
 				7CA4815919A2EF560030B30D /* test.json in CopyFiles */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		7CEA69BA1BD271920009F783 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 12;
+			dstPath = "";
+			dstSubfolderSpec = 7;
+			files = (
+				7CEA69C21BD271B20009F783 /* chat.html in CopyFiles */,
+				7CEA69BB1BD271920009F783 /* logo.png in CopyFiles */,
+				7CEA69BC1BD271920009F783 /* login.html in CopyFiles */,
+				7CEA69BD1BD271920009F783 /* test.json in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
 		18447C3E1BD14CF50011C707 /* TestSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSocket.swift; sourceTree = "<group>"; };
-		18447C401BD14E400011C707 /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = "<group>"; };
 		7C839B6E19422CFF003A6950 /* Swifter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Swifter.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		7C839B7219422CFF003A6950 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		7C839B7319422CFF003A6950 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -74,6 +95,9 @@
 		7CB102D91A1664B200CBA3B4 /* HttpHandlers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpHandlers.swift; sourceTree = "<group>"; };
 		7CB102DC1A167FFA00CBA3B4 /* DemoServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoServer.swift; sourceTree = "<group>"; };
 		7CB102DF1A17381D00CBA3B4 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = "<group>"; };
+		7CEA69A91BD270F10009F783 /* chat.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = chat.html; sourceTree = "<group>"; };
+		7CEA69AB1BD271160009F783 /* chat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = chat.swift; sourceTree = "<group>"; };
+		7CEA69C11BD271920009F783 /* SwifterOSXChat */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SwifterOSXChat; sourceTree = BUILT_PRODUCTS_DIR; };
 		98630C061A1C9A9D00478D08 /* login.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = login.html; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -92,6 +116,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		7CEA69B91BD271920009F783 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -111,6 +142,7 @@
 			children = (
 				7C839B6E19422CFF003A6950 /* Swifter.app */,
 				7CA4813B19A2EA8D0030B30D /* SwifterOSX */,
+				7CEA69C11BD271920009F783 /* SwifterOSXChat */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -139,6 +171,7 @@
 			isa = PBXGroup;
 			children = (
 				7CA4813D19A2EA8D0030B30D /* main.swift */,
+				7CEA69AB1BD271160009F783 /* chat.swift */,
 			);
 			path = SwifterOSX;
 			sourceTree = "<group>";
@@ -161,10 +194,10 @@
 		7CA4815619A2EF2B0030B30D /* Resources */ = {
 			isa = PBXGroup;
 			children = (
+				7CEA69A91BD270F10009F783 /* chat.html */,
 				98630C061A1C9A9D00478D08 /* login.html */,
 				7CB102DF1A17381D00CBA3B4 /* logo.png */,
 				7CA4815719A2EF2B0030B30D /* test.json */,
-				18447C401BD14E400011C707 /* index.html */,
 			);
 			path = Resources;
 			sourceTree = "<group>";
@@ -206,6 +239,23 @@
 			productReference = 7CA4813B19A2EA8D0030B30D /* SwifterOSX */;
 			productType = "com.apple.product-type.tool";
 		};
+		7CEA69AD1BD271920009F783 /* SwifterOSXChat */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 7CEA69BE1BD271920009F783 /* Build configuration list for PBXNativeTarget "SwifterOSXChat" */;
+			buildPhases = (
+				7CEA69AE1BD271920009F783 /* Sources */,
+				7CEA69B91BD271920009F783 /* Frameworks */,
+				7CEA69BA1BD271920009F783 /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = SwifterOSXChat;
+			productName = SwifterOSX;
+			productReference = 7CEA69C11BD271920009F783 /* SwifterOSXChat */;
+			productType = "com.apple.product-type.tool";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -239,6 +289,7 @@
 			targets = (
 				7C839B6D19422CFF003A6950 /* Swifter */,
 				7CA4813A19A2EA8D0030B30D /* SwifterOSX */,
+				7CEA69AD1BD271920009F783 /* SwifterOSXChat */,
 			);
 		};
 /* End PBXProject section */
@@ -248,10 +299,10 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7CEA69AA1BD270F10009F783 /* chat.html in Resources */,
 				7CB102E01A17381D00CBA3B4 /* logo.png in Resources */,
 				7C839B7919422CFF003A6950 /* Main.storyboard in Resources */,
 				98630C071A1C9A9D00478D08 /* login.html in Resources */,
-				18447C411BD14E400011C707 /* index.html in Resources */,
 				7C839B7B19422CFF003A6950 /* Images.xcassets in Resources */,
 				7CA4815819A2EF2B0030B30D /* test.json in Resources */,
 			);
@@ -287,12 +338,26 @@
 				7CA4815519A2EED00030B30D /* Socket.swift in Sources */,
 				7CA4815319A2EED00030B30D /* HttpServer.swift in Sources */,
 				7CA4813E19A2EA8D0030B30D /* main.swift in Sources */,
-				187F2CB71BD1501700AC8474 /* TestSocket.swift in Sources */,
 				7CB102DB1A16657200CBA3B4 /* HttpHandlers.swift in Sources */,
 				7CA4815C19A2F6A60030B30D /* HttpRequest.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		7CEA69AE1BD271920009F783 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				7CEA69AF1BD271920009F783 /* HttpResponse.swift in Sources */,
+				7CEA69B11BD271920009F783 /* HttpParser.swift in Sources */,
+				7CEA69B21BD271920009F783 /* Socket.swift in Sources */,
+				7CEA69B31BD271920009F783 /* HttpServer.swift in Sources */,
+				7CEA69B41BD271920009F783 /* chat.swift in Sources */,
+				7CEA69B61BD271920009F783 /* TestSocket.swift in Sources */,
+				7CEA69B71BD271920009F783 /* HttpHandlers.swift in Sources */,
+				7CEA69B81BD271920009F783 /* HttpRequest.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXVariantGroup section */
@@ -450,6 +515,39 @@
 			};
 			name = Release;
 		};
+		7CEA69BF1BD271920009F783 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.9;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		7CEA69C01BD271920009F783 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.9;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -480,6 +578,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		7CEA69BE1BD271920009F783 /* Build configuration list for PBXNativeTarget "SwifterOSXChat" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				7CEA69BF1BD271920009F783 /* Debug */,
+				7CEA69C01BD271920009F783 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 7C839B6619422CFF003A6950 /* Project object */;

BIN
Swifter.xcodeproj/project.xcworkspace/xcuserdata/damiankolakowski.xcuserdatad/UserInterfaceState.xcuserstate


+ 67 - 195
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -202,61 +202,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
+            timestampString = "466774667.297767"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
             startingLineNumber = "45"
             endingLineNumber = "45"
-            landmarkName = "start(_:error:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "52"
-            endingLineNumber = "52"
-            landmarkName = "start(_:error:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "54"
-            endingLineNumber = "54"
-            landmarkName = "start(_:error:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "56"
-            endingLineNumber = "56"
-            landmarkName = "start(_:error:)"
-            landmarkType = "5">
+            landmarkName = "HttpServer"
+            landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -266,12 +218,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
+            timestampString = "466774667.297767"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "60"
-            endingLineNumber = "60"
-            landmarkName = "start(_:error:)"
+            startingLineNumber = "47"
+            endingLineNumber = "47"
+            landmarkName = "start(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -282,12 +234,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
+            timestampString = "466774667.297767"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "61"
-            endingLineNumber = "61"
-            landmarkName = "start(_:error:)"
+            startingLineNumber = "48"
+            endingLineNumber = "48"
+            landmarkName = "start(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -307,22 +259,6 @@
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "84"
-            endingLineNumber = "84"
-            landmarkName = "findHandler(_:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
       <BreakpointProxy
          BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
          <BreakpointContent
@@ -339,22 +275,6 @@
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "57"
-            endingLineNumber = "57"
-            landmarkName = "start(_:error:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
       <BreakpointProxy
          BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
          <BreakpointContent
@@ -378,11 +298,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/DemoServer.swift"
-            timestampString = "456083936.619071"
+            timestampString = "466766836.996785"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "68"
-            endingLineNumber = "68"
+            startingLineNumber = "64"
+            endingLineNumber = "64"
             landmarkName = "demoServer(_:)"
             landmarkType = "7">
          </BreakpointContent>
@@ -394,12 +314,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpParser.swift"
-            timestampString = "438130358.291299"
+            timestampString = "466776187.886072"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "17"
-            endingLineNumber = "17"
-            landmarkName = "nextHttpRequest(_:error:)"
+            startingLineNumber = "19"
+            endingLineNumber = "19"
+            landmarkName = "nextHttpRequest(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -410,28 +330,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpParser.swift"
-            timestampString = "438134345.357079"
+            timestampString = "466776187.886072"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "37"
-            endingLineNumber = "37"
-            landmarkName = "nextHttpRequest(_:error:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpParser.swift"
-            timestampString = "438134345.357079"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "36"
-            endingLineNumber = "36"
-            landmarkName = "nextHttpRequest(_:error:)"
+            startingLineNumber = "18"
+            endingLineNumber = "18"
+            landmarkName = "nextHttpRequest(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -458,12 +362,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
+            timestampString = "466775670.320344"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "117"
-            endingLineNumber = "117"
-            landmarkName = "stop()"
+            startingLineNumber = "105"
+            endingLineNumber = "105"
+            landmarkName = "captureExpressionGroups(_:value:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -474,13 +378,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "465835193.153472"
+            timestampString = "466775670.320344"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "118"
-            endingLineNumber = "118"
-            landmarkName = "HttpServer"
-            landmarkType = "3">
+            startingLineNumber = "106"
+            endingLineNumber = "106"
+            landmarkName = "captureExpressionGroups(_:value:)"
+            landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -506,12 +410,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/Socket.swift"
-            timestampString = "438247668.960213"
+            timestampString = "466776889.884253"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "55"
-            endingLineNumber = "55"
-            landmarkName = "writeUTF8(_:string:error:)"
+            startingLineNumber = "28"
+            endingLineNumber = "28"
+            landmarkName = "tcpForListen(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -772,45 +676,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "Common/DemoServer.swift"
-            timestampString = "456083936.619071"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "64"
-            endingLineNumber = "64"
-            landmarkName = "demoServer(_:)"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpParser.swift"
-            timestampString = "448798148.791495"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "16"
-            endingLineNumber = "16"
-            landmarkName = "nextHttpRequest(_:error:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Common/HttpParser.swift"
-            timestampString = "448798167.471669"
+            filePath = "Swifter/ViewController.swift"
+            timestampString = "455528244.369233"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "27"
-            endingLineNumber = "27"
-            landmarkName = "nextHttpRequest(_:error:)"
+            startingLineNumber = "14"
+            endingLineNumber = "14"
+            landmarkName = "viewDidLoad()"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -820,13 +692,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "Swifter/ViewController.swift"
-            timestampString = "455528244.369233"
+            filePath = "Common/Socket.swift"
+            timestampString = "466776889.884253"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "14"
-            endingLineNumber = "14"
-            landmarkName = "viewDidLoad()"
+            startingLineNumber = "80"
+            endingLineNumber = "80"
+            landmarkName = "writeData(_:data:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -837,13 +709,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/Socket.swift"
-            timestampString = "456081776.855702"
+            timestampString = "466776889.884253"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "108"
-            endingLineNumber = "108"
-            landmarkName = "peername(_:error:)"
-            landmarkType = "5">
+            startingLineNumber = "87"
+            endingLineNumber = "87"
+            landmarkName = "Socket"
+            landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -853,12 +725,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/Socket.swift"
-            timestampString = "456083512.241377"
+            timestampString = "466776889.884253"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "115"
-            endingLineNumber = "115"
-            landmarkName = "peername(_:error:)"
+            startingLineNumber = "89"
+            endingLineNumber = "89"
+            landmarkName = "acceptClientSocket(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -868,13 +740,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "Common/Socket.swift"
-            timestampString = "456083787.052298"
+            filePath = "Common/HttpServer.swift"
+            timestampString = "466775670.320344"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
             startingLineNumber = "117"
             endingLineNumber = "117"
-            landmarkName = "peername(_:error:)"
+            landmarkName = "stop()"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -884,13 +756,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "Common/HttpResponse.swift"
-            timestampString = "465836128.222429"
+            filePath = "Common/HttpRequest.swift"
+            timestampString = "466766967.614148"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "111"
-            endingLineNumber = "111"
-            landmarkName = "headers()"
+            startingLineNumber = "20"
+            endingLineNumber = "20"
+            landmarkName = "parseForm()"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -900,13 +772,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "Common/HttpServer.swift"
-            timestampString = "465836185.461661"
+            filePath = "Common/HttpRequest.swift"
+            timestampString = "466767025.146182"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "130"
-            endingLineNumber = "130"
-            landmarkName = "respond(_:response:keepAlive:)"
+            startingLineNumber = "22"
+            endingLineNumber = "22"
+            landmarkName = "parseForm()"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>

+ 10 - 0
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -14,6 +14,11 @@
 			<key>orderHint</key>
 			<integer>0</integer>
 		</dict>
+		<key>SwifterOSX copy.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>3</integer>
+		</dict>
 		<key>SwifterOSX.xcscheme</key>
 		<dict>
 			<key>orderHint</key>
@@ -42,6 +47,11 @@
 			<key>primary</key>
 			<true/>
 		</dict>
+		<key>7CEA69AD1BD271920009F783</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
 	</dict>
 </dict>
 </plist>

+ 1 - 1
Swifter/AppDelegate.swift

@@ -1,7 +1,7 @@
 //
 //  AppDelegate.swift
 //  TestSwift
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation

+ 3 - 5
Swifter/ViewController.swift

@@ -1,7 +1,7 @@
 //
 //  ViewController.swift
 //  Swifter
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
 import UIKit
@@ -12,11 +12,9 @@ class ViewController: UIViewController {
     
     override func viewDidLoad() {
         super.viewDidLoad()
-        let server = demoServer(NSBundle.mainBundle().resourcePath)
-        self.server = server
-        
+        self.server = demoServer(NSBundle.mainBundle().resourcePath)
         do {
-            try server.start()
+            try self.server.start()
         } catch {
             print("Server start error: \(error)")
         }

+ 19 - 0
SwifterOSX/chat.swift

@@ -0,0 +1,19 @@
+//
+//  chat.swift
+//  Swifter
+//
+//  Created by Damian Kolakowski on 17/10/15.
+//  Copyright © 2015 Damian Kołakowski. All rights reserved.
+//
+
+import Foundation
+
+let server = testSocket(NSBundle.mainBundle().resourcePath!)
+
+do {
+    try server.start(9080)
+    print("Server has started ( port = 9080 ). Try to connect now...")
+    NSRunLoop.mainRunLoop().run()
+} catch {
+    print("Server start error: \(error)")
+}

+ 4 - 4
SwifterOSX/main.swift

@@ -1,16 +1,16 @@
 //
 //  main.swift
 //  SwifterOSX
-//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
 //
 
-
 import Foundation
 
-let server = testSocket(NSBundle.mainBundle().resourcePath!)
+let server = demoServer(NSBundle.mainBundle().resourcePath!)
+
 do {
     try server.start(9080)
-    print("Server started. Try a connection now...")
+    print("Server has started ( port = 9080 ). Try to connect now...")
     NSRunLoop.mainRunLoop().run()
 } catch {
     print("Server start error: \(error)")