Ver Fonte

Routing is done using regular expressions.
Added new kind of handler to serve files from selected directory.

Damian Kołakowski há 12 anos atrás
pai
commit
0008079ac6

+ 4 - 0
Swifter.xcodeproj/project.pbxproj

@@ -15,6 +15,7 @@
 		7C839B9319422D50003A6950 /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C839B9019422D50003A6950 /* HttpParser.swift */; };
 		7C839B9419422D50003A6950 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C839B9119422D50003A6950 /* HttpServer.swift */; };
 		7C839B9519422D50003A6950 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C839B9219422D50003A6950 /* Socket.swift */; };
+		7C9D35DA1989ADA4008AC163 /* test.json in Resources */ = {isa = PBXBuildFile; fileRef = 7C9D35D91989ADA4008AC163 /* test.json */; };
 		7CF6E638195203E5003635F0 /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CF6E637195203E5003635F0 /* HttpResponse.swift */; };
 /* End PBXBuildFile section */
 
@@ -41,6 +42,7 @@
 		7C839B9019422D50003A6950 /* HttpParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpParser.swift; sourceTree = "<group>"; };
 		7C839B9119422D50003A6950 /* HttpServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServer.swift; sourceTree = "<group>"; };
 		7C839B9219422D50003A6950 /* Socket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socket.swift; sourceTree = "<group>"; };
+		7C9D35D91989ADA4008AC163 /* test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = test.json; sourceTree = "<group>"; };
 		7CF6E637195203E5003635F0 /* HttpResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpResponse.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -83,6 +85,7 @@
 		7C839B7019422CFF003A6950 /* Swifter */ = {
 			isa = PBXGroup;
 			children = (
+				7C9D35D91989ADA4008AC163 /* test.json */,
 				7CF6E637195203E5003635F0 /* HttpResponse.swift */,
 				7C839B9019422D50003A6950 /* HttpParser.swift */,
 				7C839B9119422D50003A6950 /* HttpServer.swift */,
@@ -203,6 +206,7 @@
 			files = (
 				7C839B7919422CFF003A6950 /* Main.storyboard in Resources */,
 				7C839B7B19422CFF003A6950 /* Images.xcassets in Resources */,
+				7C9D35DA1989ADA4008AC163 /* test.json in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

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


+ 177 - 3
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -10,14 +10,188 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Swifter/Socket.swift"
-            timestampString = "424730662.541514"
+            timestampString = "426535422.067964"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "79"
-            endingLineNumber = "79"
+            startingLineNumber = "81"
+            endingLineNumber = "81"
             landmarkName = "nosigpipe(_:)"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpParser.swift"
+            timestampString = "425325410.974462"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "59"
+            endingLineNumber = "59"
+            landmarkName = "nextUInt8(_:)"
+            landmarkType = "5">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428448744.62127"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "31"
+            endingLineNumber = "31"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428448848.286292"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "23"
+            endingLineNumber = "23"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428453130.669041"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "52"
+            endingLineNumber = "52"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428453130.669041"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "73"
+            endingLineNumber = "73"
+            landmarkName = "start(listenPort:error:)"
+            landmarkType = "5">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428451908.791649"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "46"
+            endingLineNumber = "46"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428449961.443143"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "22"
+            endingLineNumber = "22"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
+            <Locations>
+               <Location
+                  shouldBeEnabled = "No"
+                  ignoreCount = "0"
+                  continueAfterRunningActions = "No"
+                  symbolName = "Swifter.HttpServer.subscript.getter (Swift.String) -&gt; Swift.Optional&lt;(Swift.String, Swift.Dictionary&lt;Swift.String, Swift.String&gt;) -&gt; Swifter.HttpResponse&gt;"
+                  moduleName = "Swifter"
+                  urlString = "file:///Users/damiankolakowski/Desktop/Swifter/Swifter/HttpServer.swift"
+                  timestampString = "428450085.122215"
+                  startingColumnNumber = "9223372036854775807"
+                  endingColumnNumber = "9223372036854775807"
+                  startingLineNumber = "22"
+                  endingLineNumber = "22"
+                  offsetFromSymbolStart = "30">
+               </Location>
+               <Location
+                  shouldBeEnabled = "No"
+                  ignoreCount = "0"
+                  continueAfterRunningActions = "No"
+                  symbolName = "reabstraction thunk helper from @callee_owned (@in (Swift.String, Swift.Dictionary&lt;Swift.String, Swift.String&gt;)) -&gt; (@out Swifter.HttpResponse) to @callee_owned (@owned Swift.String, @owned Swift.Dictionary&lt;Swift.String, Swift.String&gt;) -&gt; (@owned Swifter.HttpResponse)"
+                  moduleName = "Swifter"
+                  urlString = "file:///Users/damiankolakowski/Desktop/Swifter/Swifter/HttpServer.swift"
+                  timestampString = "428450085.12265"
+                  startingColumnNumber = "9223372036854775807"
+                  endingColumnNumber = "9223372036854775807"
+                  startingLineNumber = "22"
+                  endingLineNumber = "22"
+                  offsetFromSymbolStart = "33">
+               </Location>
+            </Locations>
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428452136.77327"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "46"
+            endingLineNumber = "46"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Swifter/HttpServer.swift"
+            timestampString = "428451908.791649"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "45"
+            endingLineNumber = "45"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
+         </BreakpointContent>
+      </BreakpointProxy>
    </Breakpoints>
 </Bucket>

+ 12 - 14
Swifter/AppDelegate.swift

@@ -16,27 +16,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     let server: HttpServer = HttpServer()
     
     func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
-        
-        server["/test"] = { (method, headers) in
+        server["/resources/(.+)"] = NSBundle.mainBundle().resourcePath
+        server["/test"] = { (method, url, headers) in
             var headersInfo = ""
             for (name, value) in headers {
                 headersInfo += "\(name) : \(value)<br>"
             }
-            let response = "<html><body>Method: \(method)<br>\(headersInfo)</body></html>"
+            let response = "<html><body>Url: \(url)<br>Method: \(method)<br>\(headersInfo)</body></html>"
             return .OK(.RAW(response))
         }
-        server["/json"] = { (method, headers) in
+        server["/json"] = { (method, url, headers) in
             return .OK(.JSON(["posts" : [[ "id" : 1, "message" : "hello world"],[ "id" : 2, "message" : "sample message"]], "new_updates" : false]))
         }
-        server["/redirect"] = { (method, headers) in
+        server["/redirect"] = { (method, url, headers) in
             return .MovedPermanently("http://www.google.com")
         }
-        server["/long"] = { (method, headers) in
+        server["/long"] = { (method, url, headers) in
             var longResponse = ""
             for k in 0..<1000 { longResponse += "(\(k)),->" }
             return .OK(.RAW(longResponse))
         }
-        server["/"] = { (method, headers) in
+        server["/demo"] = { (method, url, headers) in
+            return .OK(.RAW("<html><body><center><h2>Hello Swift</h2>" +
+                "<img src=\"https://devimages.apple.com.edgekey.net/swift/images/swift-hero_2x.png\"/><br>" +
+                "<h4>\(UIDevice().name), \(UIDevice().systemVersion)</h4></center></body></html>"))
+        }
+        server["/"] = { (method, url, headers) in
             var listPage = "<html><body>Available services:<br><ul>"
             for item in self.server.routes() {
                 listPage += "<li><a href=\"\(item)\">\(item)</a></li>"
@@ -44,13 +49,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
             listPage += "</ul></body></html>"
             return .OK(.RAW(listPage))
         }
-        server["/demo"] = { (method, headers) in
-            let demoPage =
-                "<html><body><center><h2>Hello Swift</h2>" +
-                "<img src=\"https://devimages.apple.com.edgekey.net/swift/images/swift-hero_2x.png\"/><br>" +
-                "<h4>\(UIDevice().name), \(UIDevice().systemVersion)</h4></center></body></html>"
-            return .OK(.RAW(demoPage))
-        }
         var error: NSError?
         if !server.start(error: &error) {
             println("Server start error: \(error)")

+ 42 - 9
Swifter/HttpServer.swift

@@ -9,23 +9,55 @@ import Foundation
 
 class HttpServer
 {
-    typealias Handler = (String, Dictionary<String,String>) -> HttpResponse
+    typealias Handler = (String, String, Dictionary<String,String>) -> HttpResponse
     
-    var handlers = Dictionary<String, Handler>()
+    var handlers: [(expression: NSRegularExpression, handler: Handler)] = []
     var acceptSocket: CInt = -1
     
-    subscript (path: String) -> Handler {
+    let matchingOptions = NSMatchingOptions(0)
+    let expressionOptions = NSRegularExpressionOptions(0)
+    
+    subscript (path: String) -> Handler? {
         get {
-            return handlers[path]!
+            for (expression, handler) in handlers {
+                let numberOfMatches: Int = expression.numberOfMatchesInString(path, options: matchingOptions, range: NSMakeRange(0, path.lengthOfBytesUsingEncoding(NSASCIIStringEncoding)))
+                if ( numberOfMatches > 0 ) {
+                    return handler
+                }
+            }
+            return nil
         }
         set ( newValue ) {
-            self.handlers.updateValue(newValue, forKey: path)
+            if let regex: NSRegularExpression = NSRegularExpression.regularExpressionWithPattern(path, options: expressionOptions, error: nil) {
+                if let newHandler = newValue {
+                    handlers += (expression: regex, handler: newHandler)
+                }
+            }
+        }
+    }
+    
+    subscript (path: String) -> String {
+        get {
+            return path
+        }
+        set ( directoryPath ) {
+            if let regex = NSRegularExpression.regularExpressionWithPattern(path, options: expressionOptions, error: nil) {
+                handlers += (expression: regex, handler: { (method, path, headers) in
+                    if let result = regex.firstMatchInString(path, options: self.matchingOptions, range: NSMakeRange(0, path.lengthOfBytesUsingEncoding(NSASCIIStringEncoding))) {
+                        let filesPath = directoryPath.stringByAppendingPathComponent(path.bridgeToObjectiveC().substringWithRange(result.rangeAtIndex(1)))
+                        if let fileBody = String.stringWithContentsOfFile(filesPath, encoding: NSASCIIStringEncoding, error: nil) {
+                            return HttpResponse.OK(.RAW(fileBody))
+                        }
+                    }
+                    return HttpResponse.NotFound
+                })
+            }
         }
     }
     
     func routes() -> Array<String> {
-        var results = Array<String>()
-        for (key,_) in handlers { results.append(key) }
+        var results = [String]()
+        for (expression,_) in handlers { results += expression.pattern }
         return results
     }
     
@@ -39,8 +71,8 @@ class HttpServer
                         let parser = HttpParser()
                         while let (path, method, headers) = parser.nextHttpRequest(socket) {
                             let keepAlive = parser.supportsKeepAlive(headers)
-                            if let handler = self.handlers[path] {
-                                HttpServer.writeResponse(socket, response: handler(method, headers), keepAlive: keepAlive)
+                            if let handler: Handler = self[path] {
+                                HttpServer.writeResponse(socket, response: handler(method, path, headers), keepAlive: keepAlive)
                             } else {
                                 HttpServer.writeResponse(socket, response: HttpResponse.NotFound, keepAlive: keepAlive)
                             }
@@ -68,6 +100,7 @@ class HttpServer
         if keepAlive {
             Socket.writeStringUTF8(socket, string: "Connection: keep-alive\r\n")
         }
+        //Socket.writeStringUTF8(socket, string: "Content-Type: text/html; charset=UTF-8\r\n")
         for (name, value) in response.headers() {
             Socket.writeStringUTF8(socket, string: "\(name): \(value)\r\n")
         }

+ 3 - 0
Swifter/test.json

@@ -0,0 +1,3 @@
+{
+	"test" : "test"
+}