소스 검색

Added shared database connection.
Simplified Process.swift class code.
Introduced middleware for HttpServerIO.

Damian Kołakowski 10 년 전
부모
커밋
3635cce414
7개의 변경된 파일92개의 추가작업 그리고 39개의 파일을 삭제
  1. 30 4
      Sources/App.swift
  2. 21 6
      Sources/HttpServerIO.swift
  3. 5 16
      Sources/Process.swift
  4. 29 7
      Sources/Reflection.swift
  5. 5 5
      Sources/SQLite.swift
  6. 2 0
      Swifter.xcodeproj/project.pbxproj
  7. 0 1
      SwifterSampleOSX/main.swift

+ 30 - 4
Sources/App.swift

@@ -10,26 +10,52 @@ import Foundation
 public class App {
     
     private let server = HttpServer()
-    
+
     public init() { }
     
-    public func run(port: in_port_t = 9080) throws -> Void {
+    public func run(port: in_port_t = 9080, _ databasePath: String) throws -> Void {
+        
+        // Open database connection.
+        
+        DatabaseReflection.sharedDatabase = try SQLite.open(databasePath)
+        
+        defer {
+            DatabaseReflection.sharedDatabase?.close()
+        }
+        
+        // Watch process signals.
+        
         Process.watchSignals { signal in
             switch signal {
-            case SIGTERM, SIGINT, SIGSTOP:
+            case SIGTERM, SIGINT:
                 self.server.stop()
+                DatabaseReflection.sharedDatabase?.close()
                 exit(EXIT_SUCCESS)
             case SIGINFO:
+                print("Swifter Version: \(HttpServer.VERSION)")
                 print(self.server.routes.joinWithSeparator("\n"))
             case SIGHUP:
                 print("//TODO - Reload config.")
             default:
-                print("signal")
+                print("Unknown signal received: \(signal).")
             }
         }
+        
+        // Add simple logging.
+        
+        self.server.middleware.append({ r in
+            print("\(r.method) - \(r.path)")
+            return nil
+        })
+        
+        // Boot the server.
+        
         print("Starting Swifter (\(HttpServer.VERSION)) at port \(port) with PID \(Process.PID)...")
+        
         try self.server.start(port)
+        
         print("Server started. Waiting for requests....")
+        
         NSRunLoop.mainRunLoop().run()
     }
 }

+ 21 - 6
Sources/HttpServerIO.swift

@@ -18,6 +18,10 @@ public class HttpServerIO {
     private var clientSockets: Set<Socket> = []
     private let clientSocketsLock = NSLock()
     
+    public typealias MiddlewareCallback = HttpRequest -> HttpResponse?
+    
+    public var middleware = [MiddlewareCallback]()
+    
     public func start(listenPort: in_port_t = 8080) throws {
         stop()
         listenSocket = try Socket.tcpSocketForListen(listenPort)
@@ -55,19 +59,21 @@ public class HttpServerIO {
         let address = try? socket.peername()
         let parser = HttpParser()
         while let request = try? parser.readHttpRequest(socket) {
-            let request = request
-            let (params, handler) = self.dispatch(request.method, path: request.path)
             request.address = address
-            request.params = params;
-            let response = handler(request)
+            var response = askMiddlewareForResponse(request)
+            if response == nil {
+                let (params, handler) = self.dispatch(request.method, path: request.path)
+                request.params = params
+                response = handler(request)
+            }
             var keepConnection = parser.supportsKeepAlive(request.headers)
             do {
-                keepConnection = try self.respond(socket, response: response, keepAlive: keepConnection)
+                keepConnection = try self.respond(socket, response: response!, keepAlive: keepConnection)
             } catch {
                 print("Failed to send response: \(error)")
                 break
             }
-            if let session = response.socketSession() {
+            if let session = response!.socketSession() {
                 session(socket)
                 break
             }
@@ -75,6 +81,15 @@ public class HttpServerIO {
         }
         socket.release()
     }
+
+    private func askMiddlewareForResponse(request: HttpRequest) -> HttpResponse? {
+        for layer in middleware {
+            if let response = layer(request) {
+                return response
+            }
+        }
+        return nil
+    }
     
     private func lock(handle: NSLock, closure: () -> ()) {
         handle.lock()

+ 5 - 16
Sources/Process.swift

@@ -18,24 +18,13 @@ public class Process {
     
     public static func watchSignals(callback: SignalCallback) {
         if !signalsObserved {
-            registerSignals()
+            [SIGTERM, SIGHUP, SIGSTOP, SIGINFO, SIGINT].forEach { item in
+                signal(item) {
+                    signum in Process.signalsWatchers.forEach { $0(signum) }
+                }
+            }
             signalsObserved = true
         }
         signalsWatchers.append(callback)
     }
-    
-    private static func handleSignal(signal: Int32) {
-        for callback in Process.signalsWatchers {
-            callback(signal)
-        }
-    }
-    
-    private static func registerSignals() {
-        signal(SIGTERM) { signum in Process.handleSignal(signum) }
-        signal(SIGHUP ) { signum in Process.handleSignal(signum) }
-        signal(SIGSTOP) { signum in Process.handleSignal(signum) }
-        signal(SIGTERM) { signum in Process.handleSignal(signum) }
-        signal(SIGINFO) { signum in Process.handleSignal(signum) }
-        signal(SIGINT ) { signum in Process.handleSignal(signum) }
-    }
 }

+ 29 - 7
Sources/Reflection.swift

@@ -16,10 +16,11 @@ public protocol DatabaseReflectionProtocol {
 
 public class DatabaseReflection: DatabaseReflectionProtocol {
     
+    static var sharedDatabase: SQLite?
+    
     public var id: UInt64? = nil
     
     required public init() { }
-    
 }
 
 public extension DatabaseReflectionProtocol {
@@ -47,6 +48,21 @@ public extension DatabaseReflectionProtocol {
         return ("\(mirror.subjectType)", fields)
     }
     
+    public func schemeWithValuesAsString() -> (String, [String: String?]) {
+        let (name, fields) = schemeWithValuesMethod2()
+        var map = [String: String?]()
+        for (key, value) in fields {
+            // TODO - Replace this by extending all supported types by a protocol.
+            // Example: 'extenstion Int: DatabaseConvertible { convert() -> something ( not necessary String type ) }'
+            if let intValue    = value as? Int    { map[key] = String(intValue) }
+            if let int32Value  = value as? Int32  { map[key] = String(int32Value) }
+            if let int64Value  = value as? Int64  { map[key] = String(int64Value) }
+            if let doubleValue = value as? Double { map[key] = String(doubleValue) }
+            if let stringValue = value as? String { map[key] = stringValue }
+        }
+        return (name, map)
+    }
+    
     public static func classInstanceWithSchemeMethod1() -> (Self, String, [String: Any?]) {
         let instance = Self()
         let (name, fields) = instance.schemeWithValuesMethod1()
@@ -65,12 +81,18 @@ public extension DatabaseReflectionProtocol {
         return instance
     }
     
-    func insert() throws {
-        // Stub.
-    }
-    
-    func update() throws {
-        // Stub.
+    public func insert() throws {
+        guard let database = DatabaseReflection.sharedDatabase else {
+            throw SQLiteError.OpenFailed("Database connection is not opened.")
+        }
+        let (name, fields) = schemeWithValuesAsString()
+        let create = "CREATE TABLE IF NOT EXISTS \(name) (" + fields.keys.map { "\($0) TEXT" }.joinWithSeparator(", ")  + ");"
+        try database.exec(create)
+        // TODO - Replace this with the binding to avoid SQL injection.
+        let ordered = fields.keys.reduce([(String, String)]()) { $0 + [($1, "\"\(fields[$1])\"")] }
+        let names = ordered.map({ $0.0 }).joinWithSeparator(", ")
+        let values = ordered.map({ $0.1 }).joinWithSeparator(", ")
+        try database.exec("INSERT INTO \(name)(" + names + ") VALUES(" + values  + ");" )
     }
     
 }

+ 5 - 5
Sources/SQLite.swift

@@ -7,7 +7,7 @@
 
 import Foundation
 
-enum SQLiteError: ErrorType {
+public enum SQLiteError: ErrorType {
     case OpenFailed(String?)
     case ExecFailed(String?)
 }
@@ -32,12 +32,12 @@ public class SQLite {
     private struct ExecCContext { var callback: ([String: String] -> Void)? }
 
     public func exec(sql: String) throws {
-        try exec(sql, callback: nil)
+        try exec(sql, nil)
     }
     
-    public func exec(sql: String, callback: ([String: String] -> Void)?) throws {
+    public func exec(sql: String, _ stepCallback: ([String: String] -> Void)?) throws {
         var errorMessagePointer = UnsafeMutablePointer<Int8>()
-        var execCContext = ExecCContext(callback: callback)
+        var execCContext = ExecCContext(callback: stepCallback)
         let execResult = sql.withCString {
             sqlite3_exec(databaseConnection, $0, { (context, count, values, names) -> Int32 in
                 var content = [String: String]()
@@ -100,7 +100,7 @@ public class SQLite {
         }
     }
     
-    public func close() throws {
+    public func close() {
         sqlite3_close(databaseConnection)
     }
 }

+ 2 - 0
Swifter.xcodeproj/project.pbxproj

@@ -1123,6 +1123,7 @@
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				METAL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
+				OTHER_SWIFT_FLAGS = "";
 				SDKROOT = appletvos;
 				SWIFT_OBJC_BRIDGING_HEADER = "Sources/sqlite-Bridging-Header.h";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -1164,6 +1165,7 @@
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				METAL_ENABLE_DEBUG_INFO = NO;
 				ONLY_ACTIVE_ARCH = NO;
+				OTHER_SWIFT_FLAGS = "";
 				SDKROOT = appletvos;
 				SWIFT_OBJC_BRIDGING_HEADER = "Sources/sqlite-Bridging-Header.h";
 				TARGETED_DEVICE_FAMILY = "1,2";

+ 0 - 1
SwifterSampleOSX/main.swift

@@ -7,7 +7,6 @@
 import Foundation
 import Swifter
 
-
 do {
     let server: HttpServer = demoServer(try File.currentWorkingDirectory())
     server["/SwiftyJSON"] = { request in