Переглянути джерело

Swift 2.0 Error Properly used
subscript of HttpServer corrected

nonn 10 роки тому
батько
коміт
4b461b4f7a
5 змінених файлів з 171 додано та 114 видалено
  1. 46 29
      Common/HttpParser.swift
  2. 42 36
      Common/HttpServer.swift
  3. 75 42
      Common/Socket.swift
  4. 4 2
      Swifter/ViewController.swift
  5. 4 5
      SwifterOSX/main.swift

+ 46 - 29
Common/HttpParser.swift

@@ -6,36 +6,55 @@
 
 import Foundation
 
-class HttpParser {
+
+enum HttpParserError : ErrorType {
+    case RecvFailed
+    case ReadBodyFailed
+    case InvalidStatusLine(String)
     
-    func err(reason: String) -> NSError {
-        return NSError(domain: "HttpParser", code: 0, userInfo: [NSLocalizedDescriptionKey : reason])
+    var value: String {
+        switch self {
+            case .RecvFailed:
+                return "recv(...) failed."
+            
+            case .ReadBodyFailed:
+                return "IO error while reading body"
+            
+            case .InvalidStatusLine(let statusLine):
+                return "Invalid status line: \(statusLine)"
+        }
     }
+}
+
+class HttpParser {
     
-    func nextHttpRequest(socket: CInt, error:NSErrorPointer = nil) -> HttpRequest? {
-        if let statusLine = nextLine(socket, error: error) {
+    func nextHttpRequest(socket: CInt) throws -> HttpRequest {
+        do {
+            let statusLine = try nextLine(socket)
             let statusTokens = statusLine.componentsSeparatedByString(" ")
             print(statusTokens)
-            if ( statusTokens.count < 3 ) {
-                if error != nil { error.memory = err("Invalid status line: \(statusLine)") }
-                return nil
+            if (statusTokens.count < 3) {
+                throw HttpParserError.InvalidStatusLine(statusLine)
             }
             let method = statusTokens[0]
             let path = statusTokens[1]
             let urlParams = extractUrlParams(path)
             // TODO extract query parameters
-            if let headers = nextHeaders(socket, error: error) {
-                // TODO detect content-type and handle:
-                // 'application/x-www-form-urlencoded' -> Dictionary
-                // 'multipart' -> Dictionary
-                if let contentLength = headers["content-length"], let contentLengthValue = Int(contentLength) {
-                    let body = nextBody(socket, size: contentLengthValue, error: error)
-                    return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: body, capturedUrlGroups: [], address: nil)
-                }
-                return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: nil, capturedUrlGroups: [], address: nil)
+            let headers = try nextHeaders(socket)
+            // TODO detect content-type and handle:
+            // 'application/x-www-form-urlencoded' -> Dictionary
+            // 'multipart' -> Dictionary
+            let body: String?
+            if let contentLengthString = headers["content-length"],
+                let contentLength = Int(contentLengthString) {
+                    body = try nextBody(socket, size: contentLength)
+            } else {
+                body = nil
             }
+            return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: body, capturedUrlGroups: [], address: nil)
+        } catch {
+            throw error
         }
-        return nil
     }
     
     private func extractUrlParams(url: String) -> [(String, String)] {
@@ -53,14 +72,13 @@ class HttpParser {
         return []
     }
     
-    private func nextBody(socket: CInt, size: Int , error:NSErrorPointer) -> String? {
+    private func nextBody(socket: CInt, size: Int) throws -> String {
         var body = ""
         var counter = 0;
-        while ( counter < size ) {
+        while (counter < size) {
             let c = nextInt8(socket)
-            if ( c < 0 ) {
-                if error != nil { error.memory = err("IO error while reading body") }
-                return nil
+            if (c < 0) {
+                throw HttpParserError.ReadBodyFailed
             }
             body.append(UnicodeScalar(c))
             counter++;
@@ -68,9 +86,9 @@ class HttpParser {
         return body
     }
     
-    private func nextHeaders(socket: CInt, error:NSErrorPointer) -> Dictionary<String, String>? {
+    private func nextHeaders(socket: CInt) throws -> Dictionary<String, String> {
         var headers = Dictionary<String, String>()
-        while let headerLine = nextLine(socket, error: error) {
+        while let headerLine = try? nextLine(socket) {
             if ( headerLine.isEmpty ) {
                 return headers
             }
@@ -86,7 +104,7 @@ class HttpParser {
                 }
             }
         }
-        return nil
+        throw HttpParserError.RecvFailed
     }
 
     private func nextInt8(socket: CInt) -> Int {
@@ -96,7 +114,7 @@ class HttpParser {
         return Int(buffer[0])
     }
     
-    private func nextLine(socket: CInt, error:NSErrorPointer) -> String? {
+    private func nextLine(socket: CInt) throws -> String {
         var characters: String = ""
         var n = 0
         repeat {
@@ -104,8 +122,7 @@ class HttpParser {
             if ( n > 13 /* CR */ ) { characters.append(Character(UnicodeScalar(n))) }
         } while ( n > 0 && n != 10 /* NL */)
         if ( n == -1 && characters.isEmpty ) {
-            if error != nil { error.memory = Socket.lastErr("recv(...) failed.") }
-            return nil
+            throw HttpParserError.RecvFailed
         }
         return characters
     }

+ 42 - 36
Common/HttpServer.swift

@@ -26,55 +26,57 @@ public class HttpServer
         get {
             return nil
         }
-        set ( newValue ) {
-            do {
-                let regex = try NSRegularExpression(pattern: path, options: expressionOptions)
-                if let newHandler = newValue {
-                    handlers.append(expression: regex, handler: newHandler)
-                }
-            } catch {
-                    
+        
+        set {
+            if let newHandler = newValue,
+               let regex = try? NSRegularExpression(pattern: path, options: expressionOptions){
+                handlers.append(expression: regex, handler: newHandler)
             }
         }
     }
     
     public func routes() -> [String] { return handlers.map { $0.0.pattern } }
     
-    public func start(listenPort: in_port_t = 8080, error: NSErrorPointer = nil) -> Bool {
+    public func start(listenPort: in_port_t = 8080) throws {
         stop()
-        if let socket = Socket.tcpForListen(listenPort, error: error) {
+        do {
+            let socket = try Socket.tcpForListen(listenPort)
             self.acceptSocket = socket
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
-                while let socket = Socket.acceptClientSocket(self.acceptSocket) {
+            
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
+                while let socket = try? Socket.acceptClientSocket(self.acceptSocket) {
                     HttpServer.lock(self.clientSocketsLock) {
                         self.clientSockets.insert(socket)
                     }
                     if self.acceptSocket == -1 { return }
-                    let socketAddress = Socket.peername(socket)
-                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
+                    let socketAddress = try? Socket.peername(socket)!
+                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
                         let parser = HttpParser()
-                        while let request = parser.nextHttpRequest(socket) {
+                        while let request = try? parser.nextHttpRequest(socket) {
                             let keepAlive = parser.supportsKeepAlive(request.headers)
+                            let response: HttpResponse
                             if let (expression, handler) = self.findHandler(request.url) {
                                 let capturedUrlsGroups = self.captureExpressionGroups(expression, value: request.url)
                                 let updatedRequest = HttpRequest(url: request.url, urlParams: request.urlParams, method: request.method, headers: request.headers, body: request.body, capturedUrlGroups: capturedUrlsGroups, address: socketAddress)
-                                HttpServer.respond(socket, response: handler(updatedRequest), keepAlive: keepAlive)
+                                response = handler(updatedRequest)
                             } else {
-                                HttpServer.respond(socket, response: HttpResponse.NotFound, keepAlive: keepAlive)
+                                response = HttpResponse.NotFound
                             }
+                            HttpServer.respond(socket, response: response, keepAlive: keepAlive)
+
                             if !keepAlive { break }
                         }
                         Socket.release(socket)
                         HttpServer.lock(self.clientSocketsLock) {
                             self.clientSockets.remove(socket)
                         }
-                    })
+                    }
                 }
                 self.stop()
-            })
-            return true
+            }
+        } catch {
+            throw error
         }
-        return false
     }
     
     func findHandler(url:String) -> (NSRegularExpression, Handler)? {
@@ -127,22 +129,26 @@ public class HttpServer
     }
     
     public class func respond(socket: CInt, response: HttpResponse, keepAlive: Bool) {
-        Socket.writeUTF8(socket, string: "HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
-        if let body = response.body() {
-            Socket.writeASCII(socket, string: "Content-Length: \(body.length)\r\n")
-        } else {
-            Socket.writeASCII(socket, string: "Content-Length: 0\r\n")
-        }
-        if keepAlive {
-            Socket.writeASCII(socket, string: "Connection: keep-alive\r\n")
-        }
-        for (name, value) in response.headers() {
-            Socket.writeASCII(socket, string: "\(name): \(value)\r\n")
-        }
-        Socket.writeASCII(socket, string: "\r\n")
-        if let body = response.body() {
-            Socket.writeData(socket, data: body)
+        do {
+            try Socket.writeUTF8(socket, string: "HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n")
+            
+            let length = response.body()?.length ?? 0
+            try Socket.writeASCII(socket, string: "Content-Length: \(length)\r\n")
+            
+            if keepAlive {
+                try Socket.writeASCII(socket, string: "Connection: keep-alive\r\n")
+            }
+            for (name, value) in response.headers() {
+                try Socket.writeASCII(socket, string: "\(name): \(value)\r\n")
+            }
+            try Socket.writeASCII(socket, string: "\r\n")
+            if let body = response.body() {
+                try Socket.writeData(socket, data: body)
+            }
+        } catch {
+            // TODO: handle error
         }
+        
     }
 }
 

+ 75 - 42
Common/Socket.swift

@@ -8,84 +8,119 @@ import Foundation
 
 /* Low level routines for POSIX sockets */
 
-struct Socket {
-        
-    static func lastErr(reason: String) -> NSError {
-        let errorCode = errno
-        if let errorText = String.fromCString(UnsafePointer(strerror(errorCode))) {
-            return NSError(domain: "SOCKET", code: Int(errorCode), userInfo: [NSLocalizedFailureReasonErrorKey : reason, NSLocalizedDescriptionKey : errorText])
+enum SocketError: ErrorType {
+    case SocketInitializationFailed
+    case SocketOptionInitializationFailed
+    case BindFailed
+    case ListenFailed
+    case WriteFailed
+    case PeerNameFailed
+    case NameInfoFailed
+    case AcceptFailed
+    
+    var message: String {
+        switch self {
+            case .SocketInitializationFailed:
+                return "socket(...) failed"
+            
+            case .SocketOptionInitializationFailed:
+                return "setsockopt(...) failed."
+            
+            case .BindFailed:
+                return "bind(...) failed."
+            
+            case .ListenFailed:
+                return "listen(...) failed."
+            
+            case .WriteFailed:
+                return "write(...) failed"
+            
+            case .PeerNameFailed:
+                return "getpeername(...) failed"
+            
+            case .NameInfoFailed:
+                return "getnameinfo(...) failed"
+            
+            case .AcceptFailed:
+                return "accept(...) failed."
         }
-        return NSError(domain: "SOCKET", code: Int(errorCode), userInfo: nil)
     }
-    
-    static func tcpForListen(port: in_port_t = 8080, error: NSErrorPointer = nil) -> CInt? {
+}
+
+let maxPendingConnection: Int32 = 20
+
+struct Socket {    
+    static func tcpForListen(port: in_port_t = 8080) throws -> CInt {
         let s = socket(AF_INET, SOCK_STREAM, 0)
         if ( s == -1 ) {
-            if error != nil { error.memory = lastErr("socket(...) failed.") }
-            return nil
+            throw SocketError.SocketInitializationFailed
         }
         var value: Int32 = 1;
         if ( setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32))) == -1 ) {
             release(s)
-            if error != nil { error.memory = lastErr("setsockopt(...) failed.") }
-            return nil
+            throw SocketError.SocketOptionInitializationFailed
         }
         nosigpipe(s)
-        var addr = sockaddr_in(sin_len: __uint8_t(sizeof(sockaddr_in)), sin_family: sa_family_t(AF_INET),
-            sin_port: port_htons(port), sin_addr: in_addr(s_addr: inet_addr("0.0.0.0")), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
+        var addr = sockaddr_in(sin_len: __uint8_t(sizeof(sockaddr_in)),
+            sin_family: sa_family_t(AF_INET),
+            sin_port: port_htons(port),
+            sin_addr: in_addr(s_addr: inet_addr("0.0.0.0")),
+            sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
         
         var sock_addr = sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
         memcpy(&sock_addr, &addr, Int(sizeof(sockaddr_in)))
         if ( bind(s, &sock_addr, socklen_t(sizeof(sockaddr_in))) == -1 ) {
             release(s)
-            if error != nil { error.memory = lastErr("bind(...) failed.") }
-            return nil
+            throw SocketError.BindFailed
         }
-        if ( listen(s, 20 /* max pending connection */ ) == -1 ) {
+        if ( listen(s, maxPendingConnection ) == -1 ) {
             release(s)
-            if error != nil { error.memory = lastErr("listen(...) failed.") }
-            return nil
+            throw SocketError.ListenFailed
         }
         return s
     }
     
-    static func writeUTF8(socket: CInt, string: String, error: NSErrorPointer = nil) -> Bool {
+    static func writeUTF8(socket: CInt, string: String) throws {
         if let nsdata = string.dataUsingEncoding(NSUTF8StringEncoding) {
-            return writeData(socket, data: nsdata, error: error)
+            do {
+                try writeData(socket, data: nsdata)
+            } catch {
+                throw error
+            }
         }
-        return true
     }
     
-    static func writeASCII(socket: CInt, string: String, error: NSErrorPointer = nil) -> Bool {
+    static func writeASCII(socket: CInt, string: String) throws {
         if let nsdata = string.dataUsingEncoding(NSASCIIStringEncoding) {
-            return writeData(socket, data: nsdata, error: error)
+            do {
+                try writeData(socket, data: nsdata)
+            } catch {
+                throw error
+            }
         }
-        return true
     }
     
-    static func writeData(socket: CInt, data: NSData, error: NSErrorPointer = nil) -> Bool {
+    static func writeData(socket: CInt, data: NSData) throws {
         var sent = 0
         let unsafePointer = UnsafePointer<UInt8>(data.bytes)
         while ( sent < data.length ) {
             let s = write(socket, unsafePointer + sent, Int(data.length - sent))
             if ( s <= 0 ) {
-                if error != nil { error.memory = lastErr("write(...) failed.") }
-                return false
+                throw SocketError.WriteFailed
             }
             sent += s
         }
-        return true
     }
     
-    static func acceptClientSocket(socket: CInt, error:NSErrorPointer = nil) -> CInt? {
-        var addr = sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), len: socklen_t = 0
+    static func acceptClientSocket(socket: CInt) throws -> CInt {
+        var addr = sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
+        var len: socklen_t = 0
         let clientSocket = accept(socket, &addr, &len)
-        if ( clientSocket != -1 ) {
-            Socket.nosigpipe(clientSocket)
-            return clientSocket
+        if ( clientSocket == -1 ) {
+            throw SocketError.AcceptFailed
         }
-        if error != nil { error.memory = lastErr("accept(...) failed.") }
-        return nil
+        Socket.nosigpipe(clientSocket)
+        return clientSocket
     }
     
     static func nosigpipe(socket: CInt) {
@@ -104,16 +139,14 @@ struct Socket {
         close(socket)
     }
     
-    static func peername(socket: CInt, error: NSErrorPointer = nil) -> String? {
+    static func peername(socket: CInt) throws -> String? {
         var addr = sockaddr(), len: socklen_t = socklen_t(sizeof(sockaddr))
         if getpeername(socket, &addr, &len) != 0 {
-            if error != nil { error.memory = lastErr("getpeername(...) failed.") }
-            return nil
+            throw SocketError.PeerNameFailed
         }
         var hostBuffer = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
         if getnameinfo(&addr, len, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST) != 0 {
-            if error != nil { error.memory = lastErr("getnameinfo(...) failed.") }
-            return nil
+            throw SocketError.NameInfoFailed
         }
         return String.fromCString(hostBuffer)
     }

+ 4 - 2
Swifter/ViewController.swift

@@ -14,8 +14,10 @@ class ViewController: UIViewController {
         super.viewDidLoad()
         let server = demoServer(NSBundle.mainBundle().resourcePath)
         self.server = server
-        var error: NSError?
-        if !server.start(error: &error) {
+        
+        do {
+            try server.start()
+        } catch {
             print("Server start error: \(error)")
         }
     }

+ 4 - 5
SwifterOSX/main.swift

@@ -7,14 +7,13 @@
 
 import Foundation
 
-var error: NSError?
 let server = demoServer(NSBundle.mainBundle().resourcePath)
-
-if !server.start(9080, error: &error) {
-    print("Server start error: \(error)")
-} else {
+do {
+    try server.start(9080)
     print("Server started. Try a connection now...")
     NSRunLoop.mainRunLoop().run()
+} catch {
+    print("Server start error: \(error)")
 }