Jelajahi Sumber

HttpServer: encapsulate HTTP response status in enum

Or to be more accurate, in a discriminated union. This sum type aims to
capture the possible HTTP response codes and their associated
information in a type-safe way.

Also, give the handler functions a type alias and rely more on type
inference.
Yawar Amin 12 tahun lalu
induk
melakukan
347ac0d6fc
2 mengubah file dengan 49 tambahan dan 22 penghapusan
  1. 12 9
      Swifter/AppDelegate.swift
  2. 37 13
      Swifter/HttpServer.swift

+ 12 - 9
Swifter/AppDelegate.swift

@@ -17,26 +17,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     
     func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
         
-        server["/"] = { () -> (Int, String) in
-            return (HttpServer.Statuses.OK, "<html><body>Hello Swift</body></html>")
+        server["/"] = {
+            return .OK("<html><body>Hello Swift</body></html>")
         }
         
-        server["/hello"] = { () -> (Int, String) in
-            return (HttpServer.Statuses.OK, "<html><body>Hello !</body></html>")
+        server["/hello"] = {
+            return .OK("<html><body>Hello !</body></html>")
         }
         
-        server["/long"] = { () -> (Int, String) in
+        server["/long"] = {
             var longResponse = ""
             for k in 0..1000 {
                 longResponse += "(\(k)),->"
             }
-            return (HttpServer.Statuses.OK, longResponse)
+            return .OK(longResponse)
         }
         
-        server["/demo"] = { () -> (Int, String) in
-            return (HttpServer.Statuses.OK, "<html><body><center><h2>Hello Swift</h2>" +
+        server["/demo"] = {
+            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=\"/demo2\"></iframe><iframe src=\"/hello\"></iframe></body></html>")
+                "<h4>\(UIDevice().name), \(UIDevice().systemVersion)</h4></center>" +
+                "<iframe src=\"/demo2\"></iframe><iframe src=\"/hello\"></iframe></body></html>"
+            return .OK(demoPage)
         }
         
         let (result, error) = server.start(8080)

+ 37 - 13
Swifter/HttpServer.swift

@@ -7,19 +7,38 @@
 
 import Foundation
 
-/* HTTP server */
+enum ResponseStatus {
+    case OK(String)
+    case NotFound
+
+    func numericValue() {
+        switch self {
+            case .OK(_):
+                return 200
+            case .NotFound:
+                return 404
+        }
+    }
 
+    func textValue() {
+        switch self {
+            case .OK(let text):
+                return text
+            case .NotFound:
+                return "Not found"
+        }
+    }
+}
+
+typealias Handler = Void -> ResponseStatus
+
+/* HTTP server */
 class HttpServer
 {
-    enum Statuses {
-        static let OK = 200
-        static let NOT_FOUND = 404
-    }
-    
-    var handlers: Dictionary<String, (Void -> (Int, String))> = Dictionary()
+    var handlers = Dictionary<String, Handler>()
     var acceptSocket: CInt = -1
     
-    subscript (path: String) -> ((Void -> (Int, String))) {
+    subscript (path: String) -> Handler {
         get {
             return handlers[path]!
         }
@@ -48,17 +67,22 @@ class HttpServer
                     let parser = HttpParser()
                     while let (path, headers) = parser.parseHttpHeader(socket) {
                         if let handler = self.handlers[path] {
-                            let (status, response) = handler()
-                            Socket.writeStringUTF8(socket, string: "HTTP/1.1 \(status)\r\n")
-                            let nsdata = response.bridgeToObjectiveC().dataUsingEncoding(NSUTF8StringEncoding)
+                            let responseStatus = handler()
+                            let responseText = responseStatus.textValue()
+                            let nsdata =
+                                responseText
+                                    .bridgeToObjectiveC()
+                                    .dataUsingEncoding(NSUTF8StringEncoding)
+
+                            Socket.writeStringUTF8(socket, string: "HTTP/1.1 \(responseStatus.numericValue())\r\n")
                             Socket.writeStringUTF8(socket, string: "Content-Length: \(nsdata.length)\r\n")
                             if parser.supportsKeepAlive(headers) {
                                 Socket.writeStringUTF8(socket, string: "Connection: keep-alive\r\n")
                             }
                             Socket.writeStringUTF8(socket, string: "\r\n")
-                            Socket.writeStringUTF8(socket, string: response)
+                            Socket.writeStringUTF8(socket, string: responseText)
                         } else {
-                            Socket.writeStringUTF8(socket, string: "HTTP/1.1 \(Statuses.NOT_FOUND)\r\n")
+                            Socket.writeStringUTF8(socket, string: "HTTP/1.1 \(ResponseStatus.NotFound.numericValue())\r\n")
                             Socket.writeStringUTF8(socket, string: "Content-Length: 0\r\n")
                             if parser.supportsKeepAlive(headers) {
                                 Socket.writeStringUTF8(socket, string: "Connection: keep-alive\r\n")