Procházet zdrojové kódy

Added PLIST and JSON data serialisation.

Damian Kołakowski před 12 roky
rodič
revize
f4e9b42c53

+ 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 */; };
+		7CF6E638195203E5003635F0 /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CF6E637195203E5003635F0 /* HttpResponse.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -40,6 +41,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>"; };
+		7CF6E637195203E5003635F0 /* HttpResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpResponse.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -81,6 +83,7 @@
 		7C839B7019422CFF003A6950 /* Swifter */ = {
 			isa = PBXGroup;
 			children = (
+				7CF6E637195203E5003635F0 /* HttpResponse.swift */,
 				7C839B9019422D50003A6950 /* HttpParser.swift */,
 				7C839B9119422D50003A6950 /* HttpServer.swift */,
 				7C839B9219422D50003A6950 /* Socket.swift */,
@@ -217,6 +220,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7CF6E638195203E5003635F0 /* HttpResponse.swift in Sources */,
 				7C839B7619422CFF003A6950 /* ViewController.swift in Sources */,
 				7C839B9319422D50003A6950 /* HttpParser.swift in Sources */,
 				7C839B9519422D50003A6950 /* Socket.swift in Sources */,

+ 41 - 0
Swifter.xcodeproj/project.xcworkspace/xcshareddata/Swifter.xccheckout

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDESourceControlProjectFavoriteDictionaryKey</key>
+	<false/>
+	<key>IDESourceControlProjectIdentifier</key>
+	<string>6F6EE92F-5058-4110-A55D-DFBC76A28E89</string>
+	<key>IDESourceControlProjectName</key>
+	<string>Swifter</string>
+	<key>IDESourceControlProjectOriginsDictionary</key>
+	<dict>
+		<key>9BA50FC1-8933-46EC-8469-51B02ED36C86</key>
+		<string>ssh://github.com/glock45/swifter.git</string>
+	</dict>
+	<key>IDESourceControlProjectPath</key>
+	<string>Swifter.xcodeproj/project.xcworkspace</string>
+	<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
+	<dict>
+		<key>9BA50FC1-8933-46EC-8469-51B02ED36C86</key>
+		<string>../..</string>
+	</dict>
+	<key>IDESourceControlProjectURL</key>
+	<string>ssh://github.com/glock45/swifter.git</string>
+	<key>IDESourceControlProjectVersion</key>
+	<integer>110</integer>
+	<key>IDESourceControlProjectWCCIdentifier</key>
+	<string>9BA50FC1-8933-46EC-8469-51B02ED36C86</string>
+	<key>IDESourceControlProjectWCConfigurations</key>
+	<array>
+		<dict>
+			<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+			<string>public.vcs.git</string>
+			<key>IDESourceControlWCCIdentifierKey</key>
+			<string>9BA50FC1-8933-46EC-8469-51B02ED36C86</string>
+			<key>IDESourceControlWCCName</key>
+			<string>Swifter</string>
+		</dict>
+	</array>
+</dict>
+</plist>

binární
Swifter.xcodeproj/project.xcworkspace/xcuserdata/damiankolakowski.xcuserdatad/UserInterfaceState.xcuserstate


+ 0 - 78
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -19,83 +19,5 @@
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Swifter/AppDelegate.swift"
-            timestampString = "424025015.113078"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "20"
-            endingLineNumber = "20"
-            landmarkName = "application(_:didFinishLaunchingWithOptions:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Swifter/HttpParser.swift"
-            timestampString = "424733642.086281"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "18"
-            endingLineNumber = "18"
-            landmarkName = "nextHttpRequest(_:error:)"
-            landmarkType = "5">
-            <Locations>
-               <Location
-                  shouldBeEnabled = "No"
-                  ignoreCount = "0"
-                  continueAfterRunningActions = "No"
-                  symbolName = "Swifter.HttpParser.(nextHttpRequest (Swifter.HttpParser) -&gt; (Swift.Int32, error : Swift.AutoreleasingUnsafePointer&lt;Swift.Optional&lt;ObjectiveC.NSError&gt;&gt;) -&gt; Swift.Optional&lt;(Swift.String, Swift.Dictionary&lt;Swift.String, Swift.String&gt;)&gt;).(closure #1)"
-                  moduleName = "Swifter"
-                  urlString = "file:///Users/damiankolakowski/Desktop/Swifter/Swifter/HttpParser.swift"
-                  timestampString = "424733433.118798"
-                  startingColumnNumber = "9223372036854775807"
-                  endingColumnNumber = "9223372036854775807"
-                  startingLineNumber = "18"
-                  endingLineNumber = "18"
-                  offsetFromSymbolStart = "19">
-               </Location>
-               <Location
-                  shouldBeEnabled = "No"
-                  ignoreCount = "0"
-                  continueAfterRunningActions = "No"
-                  symbolName = "reabstraction thunk helper from @callee_owned (@owned Swift.Character) -&gt; (@unowned Swift.Bool) to @callee_owned (@in Swift.Character) -&gt; (@out Swift.Bool)"
-                  moduleName = "Swifter"
-                  urlString = "file:///Users/damiankolakowski/Desktop/Swifter/Swifter/HttpParser.swift"
-                  timestampString = "424733433.119056"
-                  startingColumnNumber = "9223372036854775807"
-                  endingColumnNumber = "9223372036854775807"
-                  startingLineNumber = "18"
-                  endingLineNumber = "18"
-                  offsetFromSymbolStart = "21">
-               </Location>
-            </Locations>
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "Swifter/HttpParser.swift"
-            timestampString = "424734160.444636"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "25"
-            endingLineNumber = "25"
-            landmarkName = "nextHttpRequest(_:error:)"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
    </Breakpoints>
 </Bucket>

+ 13 - 13
Swifter/AppDelegate.swift

@@ -17,39 +17,39 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     
     func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
         
-        server["/"] = {
-            return .OK("<html><body>Hello Swift</body></html>")
+        
+        var c: AnyClass?
+        server["/json"] = { (method, headers) in
+            return .OK(.JSON(["posts" : [[ "id" : 1, "message" : "hello world"],[ "id" : 2, "message" : "sample message"]], "new_updates" : false]))
         }
-        server["/redirect"] = {
+        server["/redirect"] = { (method, headers) in
             return .MovedPermanently("http://www.google.com")
         }
-        server["/long"] = {
+        server["/long"] = { (method, headers) in
             var longResponse = ""
-            for k in 0..1000 {
-                longResponse += "(\(k)),->"
-            }
-            return .OK(longResponse)
+            for k in 0..1000 { longResponse += "(\(k)),->" }
+            return .OK(.RAW(longResponse))
         }
-        server["/routes"] = {
+        server["/routes"] = { (method, headers) in
             var listPage = "<html><body>Available services:<br><ul>"
             for item in self.server.routes() {
                 listPage += "<li><a href=\"\(item)\">\(item)</a></li>"
             }
             listPage += "</ul></body></html>"
-            return .OK(listPage)
+            return .OK(.RAW(listPage))
         }
-        server["/demo"] = {
+        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>" +
                 "<iframe src=\"/routes\"></iframe><iframe src=\"/hello\"></iframe></body></html>"
-            return .OK(demoPage)
+            return .OK(.RAW(demoPage))
         }
         
         var error: NSError?
         if !server.start(error: &error) {
-            NSLog("Server start error: \(error)")
+            println("Server start error: \(error)")
         }
         
         return true

+ 2 - 2
Swifter/HttpParser.swift

@@ -10,7 +10,7 @@ import Foundation
 class HttpParser {
     
     class func err(reason:String) -> NSError {
-        return NSError.errorWithDomain("HttpParser", code: 0, userInfo:[NSLocalizedFailureReasonErrorKey : reason])
+        return NSError.errorWithDomain("HTTP_PARSER", code: 0, userInfo:[NSLocalizedFailureReasonErrorKey : reason])
     }
     
     func nextHttpRequest(socket: CInt, error:NSErrorPointer = nil) -> (String, String, Dictionary<String, String>)? {
@@ -63,7 +63,7 @@ class HttpParser {
             }
         } while ( n > 0 && buff[0] != 10 /* NL */ )
         if ( n == -1 ) {
-            if error { error.memory = Socket.socketRecentError("recv(...) failed.") }
+            if error { error.memory = Socket.socketLastError("recv(...) failed.") }
             return nil
         }
         return characters

+ 98 - 0
Swifter/HttpResponse.swift

@@ -0,0 +1,98 @@
+//
+//  HttpResponse.swift
+//  Swifter
+//
+//  Created by Damian Kolakowski on 18/06/14.
+//  Copyright (c) 2014 Damian Kołakowski. All rights reserved.
+//
+
+import Foundation
+
+enum HttpResponseBody {
+    
+    case JSON(AnyObject)
+    case XML(AnyObject)
+    case PLIST(AnyObject)
+    case RAW(String)
+    
+    func data() -> String? {
+        switch self {
+        case .JSON(let object):
+            if NSJSONSerialization.isValidJSONObject(object) {
+                var serializationError: NSError?
+                if let json = NSJSONSerialization.dataWithJSONObject(object, options: NSJSONWritingOptions.PrettyPrinted, error: &serializationError) {
+                    return NSString(data: json, encoding: NSUTF8StringEncoding)
+                }
+                return "Serialisation error: \(serializationError)"
+            }
+            return "Invalid object to serialise."
+        case .XML(let data):
+            return "XML serialization not supported."
+        case .PLIST(let object):
+            let format = NSPropertyListFormat.XMLFormat_v1_0
+            if NSPropertyListSerialization.propertyList(object, isValidForFormat: format) {
+                var serializationError: NSError?
+                if let plist = NSPropertyListSerialization.dataWithPropertyList(object, format: format, options: 0, error: &serializationError) {
+                    return NSString(data: plist, encoding: NSUTF8StringEncoding)
+                }
+                return "Serialisation error: \(serializationError)"
+            }
+            return "Invalid object to serialise."
+        case .RAW(let data):
+            return data
+        }
+    }
+}
+
+enum HttpResponse {
+    
+    case OK(HttpResponseBody), Created, Accepted
+    case MovedPermanently(String)
+    case BadRequest, Unauthorized, Forbidden, NotFound
+    case InternalServerError
+    case Raw(Int,String)
+    
+    func statusCode() -> Int {
+        switch self {
+        case .OK(_)                 : return 200
+        case .Created               : return 201
+        case .Accepted              : return 202
+        case .MovedPermanently      : return 301
+        case .BadRequest            : return 400
+        case .Unauthorized          : return 401
+        case .Forbidden             : return 403
+        case .NotFound              : return 404
+        case .InternalServerError   : return 500
+        case .Raw(let code, _)      : return code
+        }
+    }
+    
+    func reasonPhrase() -> String {
+        switch self {
+        case .OK(_)                 : return "OK"
+        case .Created               : return "Created"
+        case .Accepted              : return "Accepted"
+        case .MovedPermanently      : return "Moved Permanently"
+        case .BadRequest            : return "Bad Request"
+        case .Unauthorized          : return "Unauthorized"
+        case .Forbidden             : return "Forbidden"
+        case .NotFound              : return "Not Found"
+        case .InternalServerError   : return "Internal Server Error"
+        case .Raw(_,_)              : return "Custom"
+        }
+    }
+    
+    func headers() -> Dictionary<String, String> {
+        switch self {
+        case .MovedPermanently(let location) : return [ "Location" : location ]
+        default: return Dictionary()
+        }
+    }
+    
+    func body() -> String? {
+        switch self {
+        case .OK(let body)      : return body.data()
+        default                 : return nil
+        }
+    }
+}

+ 5 - 60
Swifter/HttpServer.swift

@@ -7,62 +7,9 @@
 
 import Foundation
 
-enum Response {
-    
-    case OK(String), Created, Accepted
-    case MovedPermanently(String)
-    case BadRequest, Unauthorized, Forbidden, NotFound
-    case InternalServerError
-    case Custom(Int,String)
-
-    func statusCode() -> Int {
-        switch self {
-        case .OK(_)                 : return 200
-        case .Created               : return 201
-        case .Accepted              : return 202
-        case .MovedPermanently      : return 301
-        case .BadRequest            : return 400
-        case .Unauthorized          : return 401
-        case .Forbidden             : return 403
-        case .NotFound              : return 404
-        case .InternalServerError   : return 500
-        case .Custom(let code, _)   : return code
-        }
-    }
-
-    func reasonPhrase() -> String {
-        switch self {
-        case .OK(_)                 : return "OK"
-        case .Created               : return "Created"
-        case .Accepted              : return "Accepted"
-        case .MovedPermanently      : return "Moved Permanently"
-        case .BadRequest            : return "Bad Request"
-        case .Unauthorized          : return "Unauthorized"
-        case .Forbidden             : return "Forbidden"
-        case .NotFound              : return "Not Found"
-        case .InternalServerError   : return "Internal Server Error"
-        case .Custom(_,_)           : return "Custom"
-        }
-    }
-    
-    func headers() -> Dictionary<String, String> {
-        switch self {
-        case .MovedPermanently(let location) : return [ "Location" : location ]
-        default: return Dictionary()
-        }
-    }
-    
-    func body() -> String? {
-        switch self {
-            case .OK(let text)  : return text
-            default             : return nil
-        }
-    }
-}
-
 class HttpServer
 {
-    typealias Handler = Void -> Response
+    typealias Handler = (String, Dictionary<String,String>) -> HttpResponse
     
     var handlers = Dictionary<String, Handler>()
     var acceptSocket: CInt = -1
@@ -78,9 +25,7 @@ class HttpServer
     
     func routes() -> Array<String> {
         var results = Array<String>()
-        for (key,_) in handlers {
-            results.append(key)
-        }
+        for (key,_) in handlers { results.append(key) }
         return results
     }
     
@@ -95,9 +40,9 @@ class HttpServer
                         while let (path, method, headers) = parser.nextHttpRequest(socket) {
                             let keepAlive = parser.supportsKeepAlive(headers)
                             if let handler = self.handlers[path] {
-                                HttpServer.writeResponse(socket, response: handler(), keepAlive: keepAlive)
+                                HttpServer.writeResponse(socket, response: handler(method, headers), keepAlive: keepAlive)
                             } else {
-                                HttpServer.writeResponse(socket, response: Response.NotFound, keepAlive: keepAlive)
+                                HttpServer.writeResponse(socket, response: HttpResponse.NotFound, keepAlive: keepAlive)
                             }
                             if !keepAlive { break }
                         }
@@ -111,7 +56,7 @@ class HttpServer
         return false
     }
     
-    class func writeResponse(socket: CInt, response: Response, keepAlive: Bool) {
+    class func writeResponse(socket: CInt, response: HttpResponse, keepAlive: Bool) {
         Socket.writeStringUTF8(socket, string: "HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
         let messageBody = response.body()
         if let body = messageBody {

+ 0 - 9
Swifter/MyPlayground.playground/timeline.xctimeline

@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Timeline
-   version = "3.0">
-   <TimelineItems>
-      <LoggerValueHistoryTimelineItem
-         documentLocation = "#CharacterRangeLen=9&amp;CharacterRangeLoc=117&amp;EndingColumnNumber=41&amp;EndingLineNumber=6&amp;StartingColumnNumber=32&amp;StartingLineNumber=6&amp;Timestamp=424200144.512699">
-      </LoggerValueHistoryTimelineItem>
-   </TimelineItems>
-</Timeline>

+ 7 - 7
Swifter/Socket.swift

@@ -11,7 +11,7 @@ import Foundation
 
 struct Socket {
     
-    static func socketRecentError(reason:String) -> NSError {
+    static func socketLastError(reason:String) -> NSError {
         let code = errno
         return NSError.errorWithDomain("SOCKET", code: Int(code), userInfo:
             [NSLocalizedFailureReasonErrorKey : reason, NSLocalizedDescriptionKey : String.fromCString(strerror(code))])
@@ -20,13 +20,13 @@ struct Socket {
     static func tcpForListen(port: in_port_t = 8080, error:NSErrorPointer = nil) -> CInt? {
         let s = socket(AF_INET, SOCK_STREAM, 0)
         if ( s == -1 ) {
-            if error { error.memory = socketRecentError("socket(...) failed.") }
+            if error { error.memory = socketLastError("socket(...) failed.") }
             return nil
         }
         var value: Int32 = 1;
         if ( setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32))) == -1 ) {
             release(s)
-            if error { error.memory = socketRecentError("setsockopt(...) failed.") }
+            if error { error.memory = socketLastError("setsockopt(...) failed.") }
             return nil
         }
         nosigpipe(s)
@@ -38,12 +38,12 @@ struct Socket {
         memcpy(&sock_addr, &addr, UInt(sizeof(sockaddr_in)))
         if ( bind(s, &sock_addr, socklen_t(sizeof(sockaddr_in))) == -1 ) {
             release(s)
-            if error { error.memory = socketRecentError("bind(...) failed.") }
+            if error { error.memory = socketLastError("bind(...) failed.") }
             return nil
         }
         if ( listen(s, 20 /* max pending connection */ ) == -1 ) {
             release(s)
-            if error { error.memory = socketRecentError("listen(...) failed.") }
+            if error { error.memory = socketLastError("listen(...) failed.") }
             return nil
         }
         return s
@@ -56,7 +56,7 @@ struct Socket {
         while ( sent < nsdata.length ) {
             let s = write(socket, unsafePointer + sent, UInt(nsdata.length - sent))
             if ( s <= 0 ) {
-                if error { error.memory = socketRecentError("write(...) failed.") }
+                if error { error.memory = socketLastError("write(\(string)) failed.") }
                 return false
             }
             sent += s
@@ -71,7 +71,7 @@ struct Socket {
             Socket.nosigpipe(clientSocket)
             return clientSocket
         }
-        if error { error.memory = socketRecentError("accept(...) failed.") }
+        if error { error.memory = socketLastError("accept(...) failed.") }
         return nil
     }