ソースを参照

Server i/o moved to HttpServerIO class.
HttpServer class covers routing.

Damian Kołakowski 10 年 前
コミット
63ddbee05f

+ 10 - 2
Sources/Swifter/Const.swift

@@ -7,8 +7,16 @@
 //
 
 import Foundation
+#if os(Linux)
+    import Glibc
+    import NSLinux
+#endif
 
 struct Constants {
-    static let CR = UInt8(13)
-    static let NL = UInt8(10)
+    
+    static let VERSION      = "1.0.2"
+    static let DEFAULT_PORT = in_port_t(8080)
+    
+    static let CR           = UInt8(13)
+    static let NL           = UInt8(10)
 }

+ 3 - 6
Sources/Swifter/DemoServer.swift

@@ -17,12 +17,9 @@ public func demoServer(publicDir: String?) -> HttpServer {
 
     server["/"] = { r in
         var listPage = "Available services:<br><ul>"
-        for route in server.routes {
-            if route.method == nil || route.method! == .GET {
-                listPage += "<li><a href=\"\(route.path)\">\(route.path)</a></li>"
-            }
+        for (method, path) in server.routes {
+            listPage += "<li><a href=\"\(path)\">\(method): \(path)</a></li>"
         }
-        
         listPage += "</ul>"
         return .OK(.Html(listPage))
     }
@@ -35,7 +32,7 @@ public func demoServer(publicDir: String?) -> HttpServer {
             headersInfo += "\(name) : \(value)<br>"
         }
         var queryParamsInfo = ""
-        for (name, value) in r.urlParams {
+        for (name, value) in r.queryParams {
             queryParamsInfo += "\(name) : \(value)<br>"
         }
         var pathParamsInfo = ""

+ 8 - 14
Sources/Swifter/HttpParser.swift

@@ -14,7 +14,6 @@
 enum HttpParserError: ErrorType {
     case ReadBodyFailed(String)
     case InvalidStatusLine(String)
-    case UnknownRequestMethod(String)
 }
 
 class HttpParser {
@@ -26,20 +25,15 @@ class HttpParser {
         if statusLineTokens.count < 3 {
             throw HttpParserError.InvalidStatusLine(statusLine)
         }
-        
-        // Make sure the request is of a known type
-        guard let method = HttpRequest.Method(rawValue: statusLineTokens[0]) else {
-            throw HttpParserError.UnknownRequestMethod(statusLine)
+        var request = HttpRequest()
+        request.method = statusLineTokens[0]
+        request.url = statusLineTokens[1]
+        request.queryParams = extractUrlParams(request.url)
+        request.headers = try readHeaders(socket)
+        if let contentLength = request.headers["content-length"], let contentLengthValue = Int(contentLength) {
+            request.body = try readBody(socket, size: contentLengthValue)
         }
-        
-        let path = statusLineTokens[1]
-        let urlParams = extractUrlParams(path)
-        let headers = try readHeaders(socket)
-        if let contentLength = headers["content-length"], let contentLengthValue = Int(contentLength) {
-            let body = try readBody(socket, size: contentLengthValue)
-            return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: body, address: nil, params: [:])
-        }
-        return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: nil, address: nil, params: [:])
+        return request
     }
     
     private func extractUrlParams(url: String) -> [(String, String)] {

+ 9 - 12
Sources/Swifter/HttpRequest.swift

@@ -7,20 +7,17 @@
 import Foundation
 
 public struct HttpRequest {
-    public enum Method: String {
-        case GET, POST, PUT, DELETE
-    }
     
-    public let url: String
-    public let urlParams: [(String, String)]
-    public let method: Method
-    public let headers: [String: String]
-    public let body: [UInt8]?
-    public var address: String?
-    public var params: [String: String]
+    public var url: String = ""
+    public var queryParams: [(String, String)] = []
+    public var method: String = ""
+    public var headers: [String: String] = [:]
+    public var body: [UInt8] = []
+    public var address: String? = ""
+    public var params: [String: String] = [:]
     
     public func parseUrlencodedForm() -> [(String, String)] {
-        guard let body = body, let contentTypeHeader = headers["content-type"] else {
+        guard let contentTypeHeader = headers["content-type"] else {
             return []
         }
         let contentTypeHeaderTokens = contentTypeHeader.split(";").map { $0.trim() }
@@ -43,7 +40,7 @@ public struct HttpRequest {
     }
     
     public func parseMultiPartFormData() -> [MultiPart] {
-        guard let body = body, let contentTypeHeader = headers["content-type"] else {
+        guard let contentTypeHeader = headers["content-type"] else {
             return []
         }
         let contentTypeHeaderTokens = contentTypeHeader.split(";").map { $0.trim() }

+ 3 - 3
Sources/Swifter/HttpResponse.swift

@@ -9,7 +9,6 @@ import Foundation
 public enum SerializationError: ErrorType {
     case InvalidObject
     case NotSupported
-    case EncodingError
 }
 
 public enum HttpResponseBody {
@@ -83,7 +82,7 @@ public enum HttpResponse {
     }
     
     func headers() -> [String: String] {
-        var headers = ["Server" : "Swifter \(HttpServer.VERSION)"]
+        var headers = ["Server" : "Swifter \(Constants.VERSION)"]
         switch self {
         case .OK(let body):
             switch body {
@@ -91,7 +90,8 @@ public enum HttpResponse {
             case .Html(_)   : headers["Content-Type"] = "text/html"
             default:break
             }
-        case .MovedPermanently(let location): headers["Location"] = location
+        case .MovedPermanently(let location):
+            headers["Location"] = location
         case .RAW(_, _, let rawHeaders, _):
             if let rawHeaders = rawHeaders {
                 for (k, v) in rawHeaders {

+ 8 - 17
Sources/Swifter/HttpRouter.swift

@@ -7,35 +7,26 @@
 import Foundation
 
 public class HttpRouter {
-    private var handlers: [(method: HttpRequest.Method?, pattern: [String],
-                            handler: HttpServer.Handler)] = []
     
-    public func routes() -> [(method: HttpRequest.Method?, path: String)] {
-        return handlers.map { ($0.method, "/" + $0.pattern.joinWithSeparator("/")) }
-    }
+    private var handlers: [(String?, pattern: [String], HttpRequest -> HttpResponse)] = []
     
-    public func register(path: String, handler: HttpServer.Handler) {
-        register(nil, path: path, handler: handler)
+    public func routes() -> [(method: String?, path: String)] {
+        return handlers.map { ($0.0, "/" + $0.pattern.joinWithSeparator("/")) }
     }
     
-    public func register(method: HttpRequest.Method?, path: String, handler: HttpServer.Handler) {
+    public func register(method: String?, path: String, handler: HttpRequest -> HttpResponse) {
         handlers.append((method, path.split("/"), handler))
         handlers.sortInPlace { $0.0.pattern.count < $0.1.pattern.count }
     }
     
-    public func unregister(path: String) {
-        unregister(nil, path: path)
-    }
-    
-    public func unregister(method: HttpRequest.Method?, path: String) {
-        let p = path.split("/")
+    public func unregister(method: String?, path: String) {
+        let tokens = path.split("/")
         handlers = handlers.filter { (meth, pattern, _) -> Bool in
-            return meth != method || pattern != p
+            return meth != method || pattern != tokens
         }
     }
     
-    public func select(method: HttpRequest.Method, url: String)
-                      -> ([String: String], HttpServer.Handler)? {
+    public func select(method: String?, url: String) -> ([String: String], HttpRequest -> HttpResponse)? {
         let urlTokens = url.split("/")
         for (meth, pattern, handler) in handlers {
             if meth == nil || meth! == method {

+ 35 - 150
Sources/Swifter/HttpServer.swift

@@ -1,178 +1,63 @@
 //
-//  HttpServer.swift
+//  HttpServer2.swift
 //  Swifter
-//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
+//
+//  Created by Damian Kolakowski on 17/12/15.
+//  Copyright © 2015 Damian Kołakowski. All rights reserved.
 //
 
 import Foundation
 
-#if os(Linux)
-    import Glibc
-    import NSLinux
-#endif
-
-public class HttpServer {
-    public class MethodRouter {
-        private let method: HttpRequest.Method?
-        private let router: HttpRouter
-        
-        private init(method: HttpRequest.Method?, router: HttpRouter) {
-            self.method = method
-            self.router = router
-        }
-        
-        public subscript(path: String) -> Handler? {
-            set {
-                if let newValue = newValue {
-                    router.register(method, path: path, handler: newValue)
-                }
-                else {
-                    router.unregister(method, path: path)
-                }
-            }
-            get {
-                return nil
-            }
-        }
-    }
-    
-    static let VERSION = "1.0.2"
-    
-    public typealias Handler = HttpRequest -> HttpResponse
+public class HttpServer: HttpServerIO {
     
-    private var router = HttpRouter()
+    private let router = HttpRouter()
     
-    private var listenSocket: Socket = Socket(socketFileDescriptor: -1)
-    private var clientSockets: Set<Socket> = []
-    private let clientSocketsLock = NSLock()
-    
-    public init() {
-        anyMethod = MethodRouter(method: nil, router: router)
-        getMethod = MethodRouter(method: .GET, router: router)
-        postMethod = MethodRouter(method: .POST, router: router)
-        putMethod = MethodRouter(method: .PUT, router: router)
-        deleteMethod = MethodRouter(method: .DELETE, router: router)
+    public var routes: [(method: String?, path: String)] {
+        return router.routes()
     }
     
-    public subscript(path: String) -> Handler? {
+    public subscript(path: String) -> (HttpRequest -> HttpResponse)? {
         set {
-            if let newValue = newValue {
-                router.register(path, handler: newValue)
+            if let handler = newValue {
+                self.router.register(nil, path: path, handler: handler)
             }
             else {
-                router.unregister(path)
+                self.router.unregister(nil, path: path)
             }
         }
-        get {
-            return nil
-        }
-    }
-    
-    private var anyMethod: MethodRouter
-    public var ANY: MethodRouter {
-        return anyMethod
+        get { return nil }
     }
     
-    private var getMethod: MethodRouter
-    public var GET: MethodRouter {
-        return getMethod
-    }
+    public lazy var DELETE : Route = self.lazyBuild("DELETE")
+    public lazy var UPDATE : Route = self.lazyBuild("UPDATE")
+    public lazy var HEAD   : Route = self.lazyBuild("HEAD")
+    public lazy var POST   : Route = self.lazyBuild("POST")
+    public lazy var GET    : Route = self.lazyBuild("GET")
+    public lazy var PUT    : Route = self.lazyBuild("PUT")
     
-    private var postMethod: MethodRouter
-    public var POST: MethodRouter {
-        return postMethod
-    }
-    
-    private var putMethod: MethodRouter
-    public var PUT: MethodRouter {
-        return putMethod
-    }
-    
-    private var deleteMethod: MethodRouter
-    public var DELETE: MethodRouter {
-        return deleteMethod
-    }
-    
-    public var routes: [(method: HttpRequest.Method?, path: String)] {
-        return router.routes()
-    }
-    
-    public func start(listenPort: in_port_t = 8080) throws {
-        stop()
-        listenSocket = try Socket.tcpSocketForListen(listenPort)
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
-            while let socket = try? self.listenSocket.acceptClientSocket() {
-                HttpServer.lock(self.clientSocketsLock) {
-                    self.clientSockets.insert(socket)
-                }
-                
-                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
-                    let socketAddress = try? socket.peername()
-                    let httpParser = HttpParser()
-                    
-                    while let request = try? httpParser.readHttpRequest(socket) {
-                        let keepAlive = httpParser.supportsKeepAlive(request.headers)
-                        var response = HttpResponse.NotFound
-                        
-                        if let (params, handler) = self.router.select(request.method,
-                                                                      url: request.url) {
-                            let updatedRequest = HttpRequest(url: request.url, urlParams: request.urlParams, method: request.method, headers: request.headers, body: request.body, address: socketAddress, params: params)
-                            response = handler(updatedRequest)
-                        } else {
-                            response = HttpResponse.NotFound
-                        }
-                        
-                        do {
-                            try HttpServer.respond(socket, response: response, keepAlive: keepAlive)
-                        } catch {
-                            print("Failed to send response: \(error)")
-                            break
-                        }
-                        
-                        if !keepAlive { break }
-                    }
-                    
-                    socket.release()
-                    HttpServer.lock(self.clientSocketsLock) {
-                        self.clientSockets.remove(socket)
-                    }
+    public struct Route {
+        public let method: String
+        public let server: HttpServer
+        public subscript(path: String) -> (HttpRequest -> HttpResponse)? {
+            set {
+                if let handler = newValue {
+                    server.router.register(method, path: path, handler: handler)
+                } else {
+                    server.router.unregister(method, path: path)
                 }
             }
-            self.stop()
-        }
-    }
-    
-    public func stop() {
-        listenSocket.release()
-        HttpServer.lock(self.clientSocketsLock) {
-            for socket in self.clientSockets {
-                socket.shutdwn()
-            }
-            self.clientSockets.removeAll(keepCapacity: true)
+            get { return nil }
         }
     }
     
-    private class func lock(handle: NSLock, closure: () -> ()) {
-        handle.lock()
-        closure()
-        handle.unlock();
+    private func lazyBuild(method: String) -> Route {
+        return Route(method: method, server: self)
     }
     
-    private class func respond(socket: Socket, response: HttpResponse, keepAlive: Bool) throws {
-        try socket.writeUTF8("HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
-        
-        let length = response.body()?.count ?? 0
-        try socket.writeUTF8("Content-Length: \(length)\r\n")
-        
-        if keepAlive {
-            try socket.writeUTF8("Connection: keep-alive\r\n")
-        }
-        for (name, value) in response.headers() {
-            try socket.writeUTF8("\(name): \(value)\r\n")
-        }
-        try socket.writeUTF8("\r\n")
-        if let body = response.body() {
-            try socket.writeUInt8(body)
+    override public func select(method: String, url: String) -> ([String : String], HttpRequest -> HttpResponse) {
+        if let handler = router.select(method, url: url) {
+            return handler
         }
+        return super.select(method, url: url)
     }
 }

+ 92 - 0
Sources/Swifter/HttpServerIO.swift

@@ -0,0 +1,92 @@
+//
+//  HttpServer.swift
+//  Swifter
+//  Copyright (c) 2015 Damian Kołakowski. All rights reserved.
+//
+
+import Foundation
+
+#if os(Linux)
+    import Glibc
+    import NSLinux
+#endif
+
+public class HttpServerIO {
+    
+    private var listenSocket: Socket = Socket(socketFileDescriptor: -1)
+    private var clientSockets: Set<Socket> = []
+    private let clientSocketsLock = NSLock()
+    
+    public func start(listenPort: in_port_t = Constants.DEFAULT_PORT) throws {
+        stop()
+        listenSocket = try Socket.tcpSocketForListen(listenPort)
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
+            while let socket = try? self.listenSocket.acceptClientSocket() {
+                HttpServerIO.lock(self.clientSocketsLock) {
+                    self.clientSockets.insert(socket)
+                }
+                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
+                    let socketAddress = try? socket.peername()
+                    let httpParser = HttpParser()                    
+                    while var request = try? httpParser.readHttpRequest(socket) {
+                        let keepAlive = httpParser.supportsKeepAlive(request.headers)
+                        let (params, handler) = self.select(request.method, url: request.url)
+                        request.address = socketAddress
+                        request.params = params;
+                        let response = handler(request)
+                        do {
+                            try HttpServerIO.respond(socket, response: response, keepAlive: keepAlive)
+                        } catch {
+                            print("Failed to send response: \(error)")
+                            break
+                        }
+                        if !keepAlive { break }
+                    }
+                    socket.release()
+                    HttpServerIO.lock(self.clientSocketsLock) {
+                        self.clientSockets.remove(socket)
+                    }
+                }
+            }
+            self.stop()
+        }
+    }
+    
+    public func select(method: String, url: String) -> ([String: String], HttpRequest -> HttpResponse) {
+        return ([:], { _ in HttpResponse.NotFound })
+    }
+    
+    public func stop() {
+        listenSocket.release()
+        HttpServerIO.lock(self.clientSocketsLock) {
+            for socket in self.clientSockets {
+                socket.shutdwn()
+            }
+            self.clientSockets.removeAll(keepCapacity: true)
+        }
+    }
+    
+    private class func lock(handle: NSLock, closure: () -> ()) {
+        handle.lock()
+        closure()
+        handle.unlock();
+    }
+    
+    private class func respond(socket: Socket, response: HttpResponse, keepAlive: Bool) throws {
+        try socket.writeUTF8("HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
+        
+        let length = response.body()?.count ?? 0
+        try socket.writeUTF8("Content-Length: \(length)\r\n")
+        
+        if keepAlive {
+            try socket.writeUTF8("Connection: keep-alive\r\n")
+        }
+        for (name, value) in response.headers() {
+            try socket.writeUTF8("\(name): \(value)\r\n")
+        }
+        try socket.writeUTF8("\r\n")
+        if let body = response.body() {
+            try socket.writeUInt8(body)
+        }
+    }
+}

+ 20 - 10
Swifter.xcodeproj/project.pbxproj

@@ -15,6 +15,10 @@
 		7C47894D1C222C7F00586CD0 /* Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C47894B1C222C7F00586CD0 /* Const.swift */; };
 		7C47894E1C222C7F00586CD0 /* Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C47894B1C222C7F00586CD0 /* Const.swift */; };
 		7C47894F1C222C7F00586CD0 /* Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C47894B1C222C7F00586CD0 /* Const.swift */; };
+		7C4789561C234B3800586CD0 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4789551C234B3800586CD0 /* HttpServer.swift */; };
+		7C4789571C234B3800586CD0 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4789551C234B3800586CD0 /* HttpServer.swift */; };
+		7C4789581C234B3800586CD0 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4789551C234B3800586CD0 /* HttpServer.swift */; };
+		7C4789591C234B3800586CD0 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4789551C234B3800586CD0 /* HttpServer.swift */; };
 		7C67C3471C17542E007B98E8 /* HttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C67C3461C17542E007B98E8 /* HttpRouter.swift */; };
 		7C67C3481C17542E007B98E8 /* HttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C67C3461C17542E007B98E8 /* HttpRouter.swift */; };
 		7C67C3491C17542E007B98E8 /* HttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C67C3461C17542E007B98E8 /* HttpRouter.swift */; };
@@ -57,10 +61,10 @@
 		7CEAF8641C14B29B003252DE /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8501C14B29B003252DE /* HttpResponse.swift */; };
 		7CEAF8651C14B29B003252DE /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8501C14B29B003252DE /* HttpResponse.swift */; };
 		7CEAF8661C14B29B003252DE /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8501C14B29B003252DE /* HttpResponse.swift */; };
-		7CEAF8671C14B29B003252DE /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServer.swift */; };
-		7CEAF8681C14B29B003252DE /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServer.swift */; };
-		7CEAF8691C14B29B003252DE /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServer.swift */; };
-		7CEAF86A1C14B29B003252DE /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServer.swift */; };
+		7CEAF8671C14B29B003252DE /* HttpServerIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServerIO.swift */; };
+		7CEAF8681C14B29B003252DE /* HttpServerIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServerIO.swift */; };
+		7CEAF8691C14B29B003252DE /* HttpServerIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServerIO.swift */; };
+		7CEAF86A1C14B29B003252DE /* HttpServerIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8511C14B29B003252DE /* HttpServerIO.swift */; };
 		7CEAF86B1C14B29B003252DE /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8521C14B29B003252DE /* Socket.swift */; };
 		7CEAF86C1C14B29B003252DE /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8521C14B29B003252DE /* Socket.swift */; };
 		7CEAF86D1C14B29B003252DE /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEAF8521C14B29B003252DE /* Socket.swift */; };
@@ -111,6 +115,7 @@
 		7AE893FF1C0512C400A29F63 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		7AE8940C1C05151100A29F63 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
 		7C47894B1C222C7F00586CD0 /* Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Const.swift; sourceTree = "<group>"; };
+		7C4789551C234B3800586CD0 /* HttpServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServer.swift; sourceTree = "<group>"; };
 		7C67C3461C17542E007B98E8 /* HttpRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpRouter.swift; sourceTree = "<group>"; };
 		7C7488771C1DA07300CBCD77 /* file.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = file.html; sourceTree = "<group>"; };
 		7C839B6E19422CFF003A6950 /* SwifterSampleiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwifterSampleiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -129,7 +134,7 @@
 		7CEAF84E1C14B29B003252DE /* HttpParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpParser.swift; sourceTree = "<group>"; };
 		7CEAF84F1C14B29B003252DE /* HttpRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpRequest.swift; sourceTree = "<group>"; };
 		7CEAF8501C14B29B003252DE /* HttpResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpResponse.swift; sourceTree = "<group>"; };
-		7CEAF8511C14B29B003252DE /* HttpServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServer.swift; sourceTree = "<group>"; };
+		7CEAF8511C14B29B003252DE /* HttpServerIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServerIO.swift; sourceTree = "<group>"; };
 		7CEAF8521C14B29B003252DE /* Socket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socket.swift; sourceTree = "<group>"; };
 		7CEAF86F1C14B2B5003252DE /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
 		98630C061A1C9A9D00478D08 /* login.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = login.html; sourceTree = "<group>"; };
@@ -261,9 +266,10 @@
 				7C47894B1C222C7F00586CD0 /* Const.swift */,
 				7CEAF84E1C14B29B003252DE /* HttpParser.swift */,
 				7CEAF8501C14B29B003252DE /* HttpResponse.swift */,
-				7CEAF8511C14B29B003252DE /* HttpServer.swift */,
+				7CEAF8511C14B29B003252DE /* HttpServerIO.swift */,
 				7CEAF8521C14B29B003252DE /* Socket.swift */,
 				7CD6DAB81C15C48500A04931 /* String+Misc.swift */,
+				7C4789551C234B3800586CD0 /* HttpServer.swift */,
 			);
 			path = Swifter;
 			sourceTree = "<group>";
@@ -446,9 +452,10 @@
 			buildActionMask = 2147483647;
 			files = (
 				7CEAF8651C14B29B003252DE /* HttpResponse.swift in Sources */,
+				7C4789581C234B3800586CD0 /* HttpServer.swift in Sources */,
 				7CEAF86D1C14B29B003252DE /* Socket.swift in Sources */,
 				7CEAF8591C14B29B003252DE /* HttpHandlers.swift in Sources */,
-				7CEAF8691C14B29B003252DE /* HttpServer.swift in Sources */,
+				7CEAF8691C14B29B003252DE /* HttpServerIO.swift in Sources */,
 				7CEAF8611C14B29B003252DE /* HttpRequest.swift in Sources */,
 				7CEAF85D1C14B29B003252DE /* HttpParser.swift in Sources */,
 				7CEAF8551C14B29B003252DE /* DemoServer.swift in Sources */,
@@ -463,9 +470,10 @@
 			buildActionMask = 2147483647;
 			files = (
 				7CEAF8661C14B29B003252DE /* HttpResponse.swift in Sources */,
+				7C4789591C234B3800586CD0 /* HttpServer.swift in Sources */,
 				7CEAF86E1C14B29B003252DE /* Socket.swift in Sources */,
 				7CEAF85A1C14B29B003252DE /* HttpHandlers.swift in Sources */,
-				7CEAF86A1C14B29B003252DE /* HttpServer.swift in Sources */,
+				7CEAF86A1C14B29B003252DE /* HttpServerIO.swift in Sources */,
 				7CEAF8621C14B29B003252DE /* HttpRequest.swift in Sources */,
 				7CEAF85E1C14B29B003252DE /* HttpParser.swift in Sources */,
 				7CEAF8561C14B29B003252DE /* DemoServer.swift in Sources */,
@@ -481,10 +489,11 @@
 			files = (
 				7CDAB8161BE2A1D400C8A977 /* ViewController.swift in Sources */,
 				7CEAF8531C14B29B003252DE /* DemoServer.swift in Sources */,
-				7CEAF8671C14B29B003252DE /* HttpServer.swift in Sources */,
+				7CEAF8671C14B29B003252DE /* HttpServerIO.swift in Sources */,
 				7CD6DAB91C15C48500A04931 /* String+Misc.swift in Sources */,
 				7C67C3471C17542E007B98E8 /* HttpRouter.swift in Sources */,
 				7C47894C1C222C7F00586CD0 /* Const.swift in Sources */,
+				7C4789561C234B3800586CD0 /* HttpServer.swift in Sources */,
 				7CEAF8571C14B29B003252DE /* HttpHandlers.swift in Sources */,
 				7CEAF85B1C14B29B003252DE /* HttpParser.swift in Sources */,
 				7CEAF86B1C14B29B003252DE /* Socket.swift in Sources */,
@@ -500,10 +509,11 @@
 			files = (
 				7CA4813E19A2EA8D0030B30D /* main.swift in Sources */,
 				7CEAF8541C14B29B003252DE /* DemoServer.swift in Sources */,
-				7CEAF8681C14B29B003252DE /* HttpServer.swift in Sources */,
+				7CEAF8681C14B29B003252DE /* HttpServerIO.swift in Sources */,
 				7CD6DABA1C15C48500A04931 /* String+Misc.swift in Sources */,
 				7C67C3481C17542E007B98E8 /* HttpRouter.swift in Sources */,
 				7C47894D1C222C7F00586CD0 /* Const.swift in Sources */,
+				7C4789571C234B3800586CD0 /* HttpServer.swift in Sources */,
 				7CEAF8581C14B29B003252DE /* HttpHandlers.swift in Sources */,
 				7CEAF85C1C14B29B003252DE /* HttpParser.swift in Sources */,
 				7CEAF86C1C14B29B003252DE /* Socket.swift in Sources */,

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


+ 112 - 64
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -821,12 +821,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRouter.swift"
-            timestampString = "471300719.120407"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "22"
-            endingLineNumber = "22"
-            landmarkName = "unregister(_:)"
+            startingLineNumber = "18"
+            endingLineNumber = "18"
+            landmarkName = "register(_:path:handler:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -837,12 +837,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRouter.swift"
-            timestampString = "471300719.576639"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "23"
-            endingLineNumber = "23"
-            landmarkName = "unregister(_:)"
+            startingLineNumber = "19"
+            endingLineNumber = "19"
+            landmarkName = "register(_:path:handler:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -885,11 +885,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpParser.swift"
-            timestampString = "471302639.772698"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "22"
-            endingLineNumber = "22"
+            startingLineNumber = "21"
+            endingLineNumber = "21"
             landmarkName = "readHttpRequest(_:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -997,13 +997,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471703999.441988"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "20"
-            endingLineNumber = "20"
-            landmarkName = "parseUrlencodedForm()"
-            landmarkType = "5">
+            startingLineNumber = "17"
+            endingLineNumber = "17"
+            landmarkName = "HttpRequest"
+            landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -1013,13 +1013,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471999560.104501"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "43"
-            endingLineNumber = "43"
-            landmarkName = "parseMultiPartFormData()"
-            landmarkType = "5">
+            startingLineNumber = "40"
+            endingLineNumber = "40"
+            landmarkName = "MultiPart"
+            landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -1029,12 +1029,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471999560.104501"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "64"
-            endingLineNumber = "64"
-            landmarkName = "parseMultiPartFormData(_:boundary:)"
+            startingLineNumber = "61"
+            endingLineNumber = "61"
+            landmarkName = "parseMultiPartFormData()"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -1045,13 +1045,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471999560.104501"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "74"
-            endingLineNumber = "74"
-            landmarkName = "nextMultiPart(_:boundary:isFirst:)"
-            landmarkType = "5">
+            startingLineNumber = "71"
+            endingLineNumber = "71"
+            landmarkName = "HttpRequest"
+            landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -1061,12 +1061,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471999560.104501"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "94"
-            endingLineNumber = "94"
-            landmarkName = "nextMultiPartLine(_:)"
+            startingLineNumber = "91"
+            endingLineNumber = "91"
+            landmarkName = "nextMultiPart(_:boundary:isFirst:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -1077,11 +1077,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471999560.104501"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "103"
-            endingLineNumber = "103"
+            startingLineNumber = "100"
+            endingLineNumber = "100"
             landmarkName = "nextMultiPartLine(_:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -1093,12 +1093,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471999553.189885"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "73"
-            endingLineNumber = "73"
-            landmarkName = "nextMultiPart(_:boundary:isFirst:)"
+            startingLineNumber = "70"
+            endingLineNumber = "70"
+            landmarkName = "parseMultiPartFormData(_:boundary:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -1109,12 +1109,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "471999618.576419"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "107"
-            endingLineNumber = "107"
-            landmarkName = "nextMultiPartBody(_:boundary:)"
+            startingLineNumber = "104"
+            endingLineNumber = "104"
+            landmarkName = "nextMultiPartLine(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
@@ -1125,11 +1125,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "472000979.164829"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "121"
-            endingLineNumber = "121"
+            startingLineNumber = "118"
+            endingLineNumber = "118"
             landmarkName = "nextMultiPartBody(_:boundary:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -1141,11 +1141,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "472000979.164829"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "115"
-            endingLineNumber = "115"
+            startingLineNumber = "112"
+            endingLineNumber = "112"
             landmarkName = "nextMultiPartBody(_:boundary:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -1157,11 +1157,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/DemoServer.swift"
-            timestampString = "472000698.250372"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "56"
-            endingLineNumber = "56"
+            startingLineNumber = "53"
+            endingLineNumber = "53"
             landmarkName = "demoServer(_:)"
             landmarkType = "7">
             <Locations>
@@ -1205,11 +1205,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "472000974.81569"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "114"
-            endingLineNumber = "114"
+            startingLineNumber = "111"
+            endingLineNumber = "111"
             landmarkName = "nextMultiPartBody(_:boundary:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -1221,11 +1221,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpRequest.swift"
-            timestampString = "472001106.790404"
+            timestampString = "472077470.733846"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "111"
-            endingLineNumber = "111"
+            startingLineNumber = "108"
+            endingLineNumber = "108"
             landmarkName = "nextMultiPartBody(_:boundary:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -1269,14 +1269,62 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Sources/Swifter/HttpResponse.swift"
-            timestampString = "472002427.39139"
+            timestampString = "472077470.733846"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "80"
+            endingLineNumber = "80"
+            landmarkName = "reasonPhrase()"
+            landmarkType = "5">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Sources/Swifter/HttpServerIO.swift"
+            timestampString = "472077581.497308"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "81"
-            endingLineNumber = "81"
-            landmarkName = "data()"
+            startingLineNumber = "31"
+            endingLineNumber = "31"
+            landmarkName = "start(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Sources/Swifter/HttpServer.swift"
+            timestampString = "472077589.417334"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "41"
+            endingLineNumber = "41"
+            landmarkName = "Route"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Sources/Swifter/HttpServer.swift"
+            timestampString = "472077591.266882"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "44"
+            endingLineNumber = "44"
+            landmarkName = "Route"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
    </Breakpoints>
 </Bucket>