Răsfoiți Sursa

[Socket] Server related functions moved to Socket+Server swift file.
[Socket] Added a utility method to check what's the address type used be server's socket.
[HttpServerIO] Removed NSLock dependency.
[HttpServerIO] Server status replaced by a boolean flag - 'running'.
[HttpServerIO] port property replaced by port function.
[HttpServerIO] forcedIPv4 property replaced by isIPv4 function.

Damian Kołakowski 10 ani în urmă
părinte
comite
f593bf3553

+ 24 - 76
Sources/HttpServerIO.swift

@@ -13,80 +13,48 @@
 
 public class HttpServerIO {
     
-    public enum ServerStatus {
-        case Stopped
-        case Running
-    }
-    
-    private var listenSocket: Socket = Socket(socketFileDescriptor: -1)
-    private var listenPort: in_port_t = 8080
-    private var ipv4 = false
-    private var listenPriority: Int = DISPATCH_QUEUE_PRIORITY_BACKGROUND
-    private var serverStatus: ServerStatus = .Stopped
+    private var socket = Socket(socketFileDescriptor: -1)
+    private var sockets = Set<Socket>()
     
-    private var clientSockets: Set<Socket> = []
-    private let clientSocketsLock = NSLock()
+    public private(set) var running = false
     
-    // Returns the port used by the server for listening connection.
-    public var port: Int {
-        get {
-            return Int(listenPort)
-        }
+    public func port() throws -> Int {
+        return Int(try socket.port())
     }
     
-    // True if the IPv4 has been forced on start.
-    public var forcedIPv4: Bool {
-        get {
-            return ipv4
-        }
+    public func isIPv4() throws -> Bool {
+        return try socket.isIPv4()
     }
     
-    // Returns the priority used for dispatch
-    public var priority: Int {
-        get {
-            return listenPriority
-        }
-    }
-    
-    // Returns the server status (Running or not).
-    public var status: ServerStatus {
-        get {
-            return serverStatus
-        }
+    deinit {
+        stop()
     }
     
     public func start(port: in_port_t = 8080, forceIPv4: Bool = false, priority: Int = DISPATCH_QUEUE_PRIORITY_BACKGROUND) throws {
         stop()
-        self.listenSocket = try Socket.tcpSocketForListen(port, forceIPv4: forceIPv4)
-        self.listenPort = try self.listenSocket.port()
-        self.ipv4 = forceIPv4
-        self.listenPriority = priority
+        self.socket = try Socket.tcpSocketForListen(port, forceIPv4: forceIPv4)
+        self.running = true
         dispatch_async(dispatch_get_global_queue(priority, 0)) {
-            self.serverStatus = .Running
-            while let socket = try? self.listenSocket.acceptClientSocket() {
-                self.lock(self.clientSocketsLock) {
-                    self.clientSockets.insert(socket)
-                }
+            while let socket = try? self.socket.acceptClientSocket() {
                 dispatch_async(dispatch_get_global_queue(priority, 0), {
+                    self.sockets.insert(socket)
                     self.handleConnection(socket)
-                    self.lock(self.clientSocketsLock) {
-                        self.clientSockets.remove(socket)
-                    }
+                    self.sockets.remove(socket)
                 })
             }
             self.stop()
-            self.serverStatus = .Stopped
+            self.running = false
         }
     }
     
     public func stop() {
-        listenSocket.release()
-        lock(self.clientSocketsLock) {
-            for socket in self.clientSockets {
-                socket.shutdwn()
-            }
-            self.clientSockets.removeAll(keepCapacity: true)
+        // Shutdown connected peers because they can live in 'keep-alive' or 'websocket' loops.
+        for socket in self.sockets {
+            socket.shutdwn()
         }
+        self.sockets.removeAll(keepCapacity: true)
+        socket.release()
+        self.running = false
     }
     
     public func dispatch(request: HttpRequest) -> ([String: String], HttpRequest -> HttpResponse) {
@@ -94,13 +62,12 @@ public class HttpServerIO {
     }
     
     private func handleConnection(socket: Socket) {
-        let address = try? socket.peername()
         let parser = HttpParser()
         while let request = try? parser.readHttpRequest(socket) {
             let request = request
-            request.address = address
+            request.address = try? socket.peername()
             let (params, handler) = self.dispatch(request)
-            request.params = params;
+            request.params = params
             let response = handler(request)
             var keepConnection = parser.supportsKeepAlive(request.headers)
             do {
@@ -118,13 +85,8 @@ public class HttpServerIO {
         socket.release()
     }
     
-    private func lock(handle: NSLock, closure: () -> ()) {
-        handle.lock()
-        closure()
-        handle.unlock();
-    }
-    
     private struct InnerWriteContext: HttpResponseBodyWriter {
+        
         let socket: Socket
 
         func write(file: File) throws {
@@ -170,20 +132,6 @@ public class HttpServerIO {
 
 #if os(Linux)
 
-public class NSLock {
-    
-    private var mutex = pthread_mutex_t()
-    
-    init() { pthread_mutex_init(&mutex, nil) }
-    
-    public func lock() { pthread_mutex_lock(&mutex) }
-    
-    public func unlock() { pthread_mutex_unlock(&mutex) }
-    
-    deinit { pthread_mutex_destroy(&mutex) }
-}
-
-
 let DISPATCH_QUEUE_PRIORITY_BACKGROUND = 0
 
 private class dispatch_context {

+ 98 - 0
Sources/Socket+Server.swift

@@ -0,0 +1,98 @@
+//
+//  Socket+Server.swift
+//  Swifter
+//
+//  Created by Damian Kolakowski on 13/07/16.
+//
+
+#if os(Linux)
+    import Glibc
+#else
+    import Foundation
+#endif
+
+extension Socket {
+    
+    public class func tcpSocketForListen(port: in_port_t, forceIPv4: Bool = false, maxPendingConnection: Int32 = SOMAXCONN) throws -> Socket {
+        
+        #if os(Linux)
+            let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, Int32(SOCK_STREAM.rawValue), 0)
+        #else
+            let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, SOCK_STREAM, 0)
+        #endif
+        
+        if socketFileDescriptor == -1 {
+            throw SocketError.SocketCreationFailed(Errno.description())
+        }
+        
+        var value: Int32 = 1
+        if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32))) == -1 {
+            let details = Errno.description()
+            Socket.release(socketFileDescriptor)
+            throw SocketError.SocketSettingReUseAddrFailed(details)
+        }
+        Socket.setNoSigPipe(socketFileDescriptor)
+        
+        #if os(Linux)
+            var bindResult: Int32 = -1
+            if forceIPv4 {
+                var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
+                                       sin_port: port.bigEndian,
+                                       sin_addr: in_addr(s_addr: in_addr_t(0)),
+                                       sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
+                
+                bindResult = withUnsafePointer(&addr) {
+                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in)))
+                }
+            } else {
+                var addr = sockaddr_in6(sin6_family: sa_family_t(AF_INET6),
+                                        sin6_port: port.bigEndian,
+                                        sin6_flowinfo: 0,
+                                        sin6_addr: in6addr_any,
+                                        sin6_scope_id: 0)
+                
+                bindResult = withUnsafePointer(&addr) {
+                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6)))
+                }
+            }
+        #else
+            var bindResult: Int32 = -1
+            if forceIPv4 {
+                var addr = sockaddr_in(sin_len: UInt8(strideof(sockaddr_in)),
+                                       sin_family: UInt8(AF_INET),
+                                       sin_port: port.bigEndian,
+                                       sin_addr: in_addr(s_addr: in_addr_t(0)),
+                                       sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
+                
+                bindResult = withUnsafePointer(&addr) {
+                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in)))
+                }
+            } else {
+                var addr = sockaddr_in6(sin6_len: UInt8(strideof(sockaddr_in6)),
+                                        sin6_family: UInt8(AF_INET6),
+                                        sin6_port: port.bigEndian,
+                                        sin6_flowinfo: 0,
+                                        sin6_addr: in6addr_any,
+                                        sin6_scope_id: 0)
+                
+                bindResult = withUnsafePointer(&addr) {
+                    bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6)))
+                }
+            }
+        #endif
+        
+        if bindResult == -1 {
+            let details = Errno.description()
+            Socket.release(socketFileDescriptor)
+            throw SocketError.BindFailed(details)
+        }
+        
+        if listen(socketFileDescriptor, maxPendingConnection ) == -1 {
+            let details = Errno.description()
+            Socket.release(socketFileDescriptor)
+            throw SocketError.ListenFailed(details)
+        }
+        return Socket(socketFileDescriptor: socketFileDescriptor)
+    }
+
+}

+ 14 - 77
Sources/Socket.swift

@@ -28,81 +28,7 @@ public enum SocketError: ErrorType {
 }
 
 public class Socket: Hashable, Equatable {
-    
-    public class func tcpSocketForListen(port: in_port_t, forceIPv4: Bool = false, maxPendingConnection: Int32 = SOMAXCONN) throws -> Socket {
-        
-        #if os(Linux)
-            let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, Int32(SOCK_STREAM.rawValue), 0)
-        #else
-            let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, SOCK_STREAM, 0)
-        #endif
-        
-        if socketFileDescriptor == -1 {
-            throw SocketError.SocketCreationFailed(Errno.description())
-        }
         
-        var value: Int32 = 1
-        if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32))) == -1 {
-            let details = Errno.description()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.SocketSettingReUseAddrFailed(details)
-        }
-        Socket.setNoSigPipe(socketFileDescriptor)
-        
-        #if os(Linux)
-            var bindResult: Int32 = -1
-            if forceIPv4 {
-                var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
-                    sin_port: port.bigEndian,
-                    sin_addr: in_addr(s_addr: in_addr_t(0)),
-                    sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
-                
-                bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in))) }
-            } else {
-                var addr = sockaddr_in6(sin6_family: sa_family_t(AF_INET6),
-                    sin6_port: port.bigEndian,
-                    sin6_flowinfo: 0,
-                    sin6_addr: in6addr_any,
-                    sin6_scope_id: 0)
-                
-                bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6))) }
-            }
-        #else
-            var bindResult: Int32 = -1
-            if forceIPv4 {
-                var addr = sockaddr_in(sin_len: UInt8(strideof(sockaddr_in)),
-                    sin_family: UInt8(AF_INET),
-                    sin_port: port.bigEndian,
-                    sin_addr: in_addr(s_addr: in_addr_t(0)),
-                    sin_zero:(0, 0, 0, 0, 0, 0, 0, 0))
-             
-                bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in))) }
-            } else {
-                var addr = sockaddr_in6(sin6_len: UInt8(strideof(sockaddr_in6)),
-                    sin6_family: UInt8(AF_INET6),
-                    sin6_port: port.bigEndian,
-                    sin6_flowinfo: 0,
-                    sin6_addr: in6addr_any,
-                    sin6_scope_id: 0)
-                
-                bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6))) }
-            }
-        #endif
-
-        if bindResult == -1 {
-            let details = Errno.description()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.BindFailed(details)
-        }
-        
-        if listen(socketFileDescriptor, maxPendingConnection ) == -1 {
-            let details = Errno.description()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.ListenFailed(details)
-        }
-        return Socket(socketFileDescriptor: socketFileDescriptor)
-    }
-    
     let socketFileDescriptor: Int32
     
     public init(socketFileDescriptor: Int32) {
@@ -149,6 +75,17 @@ public class Socket: Hashable, Equatable {
         }
     }
     
+    public func isIPv4() throws -> Bool {
+        var addr = sockaddr_in()
+        return try withUnsafePointer(&addr) { pointer in
+            var len = socklen_t(sizeof(sockaddr_in))
+            if getsockname(socketFileDescriptor, UnsafeMutablePointer(pointer), &len) != 0 {
+                throw SocketError.GetSockNameFailed(Errno.description())
+            }
+            return Int32(addr.sin_family) == AF_INET
+        }
+    }
+    
     public func writeUTF8(string: String) throws {
         try writeUInt8(ArraySlice(string.utf8))
     }
@@ -211,7 +148,7 @@ public class Socket: Hashable, Equatable {
         return name
     }
     
-    private class func setNoSigPipe(socket: Int32) {
+    public class func setNoSigPipe(socket: Int32) {
         #if os(Linux)
             // There is no SO_NOSIGPIPE in Linux (nor some other systems). You can instead use the MSG_NOSIGNAL flag when calling send(),
             // or use signal(SIGPIPE, SIG_IGN) to make your entire application ignore SIGPIPE.
@@ -222,7 +159,7 @@ public class Socket: Hashable, Equatable {
         #endif
     }
     
-    private class func shutdwn(socket: Int32) {
+    public class func shutdwn(socket: Int32) {
         #if os(Linux)
             shutdown(socket, Int32(SHUT_RDWR))
         #else
@@ -230,7 +167,7 @@ public class Socket: Hashable, Equatable {
         #endif
     }
     
-    private class func release(socket: Int32) {
+    public class func release(socket: Int32) {
         #if os(Linux)
             shutdown(socket, Int32(SHUT_RDWR))
         #else

+ 8 - 0
XCode/Swifter.xcodeproj/project.pbxproj

@@ -29,6 +29,9 @@
 		7AE893EA1C05127900A29F63 /* SwifteriOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AE893E91C05127900A29F63 /* SwifteriOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		7AE893FE1C0512C400A29F63 /* SwifterMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AE893FD1C0512C400A29F63 /* SwifterMac.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		7AE8940D1C05151100A29F63 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AE8940C1C05151100A29F63 /* Launch Screen.storyboard */; };
+		7C458EFC1D4A7526006A68E5 /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */; };
+		7C458EFD1D4A7526006A68E5 /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */; };
+		7C458EFE1D4A7526006A68E5 /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */; };
 		7C4785E91C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4785E81C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift */; };
 		7C4785EA1C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4785E81C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift */; };
 		7C71C5B11A1EC49B00682BF0 /* logo.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7CB102DF1A17381D00CBA3B4 /* logo.png */; };
@@ -137,6 +140,7 @@
 		7AE893FD1C0512C400A29F63 /* SwifterMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwifterMac.h; sourceTree = "<group>"; };
 		7AE893FF1C0512C400A29F63 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		7AE8940C1C05151100A29F63 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
+		7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Socket+Server.swift"; sourceTree = "<group>"; };
 		7C4785E81C71D15600A9FE73 /* SwifterTestsWebSocketSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsWebSocketSession.swift; sourceTree = "<group>"; };
 		7C76B29E1D369BEC00D35BFB /* Socket+File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Socket+File.swift"; sourceTree = "<group>"; };
 		7C76B2A11D369C9D00D35BFB /* Errno.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errno.swift; sourceTree = "<group>"; };
@@ -271,6 +275,7 @@
 				7C76B6F41D2C44F30030FC98 /* Scopes.swift */,
 				7C76B6F51D2C44F30030FC98 /* Socket.swift */,
 				7C76B29E1D369BEC00D35BFB /* Socket+File.swift */,
+				7C458EFB1D4A7526006A68E5 /* Socket+Server.swift */,
 				7C76B6F61D2C44F30030FC98 /* String+BASE64.swift */,
 				7C76B6F71D2C44F30030FC98 /* String+Misc.swift */,
 				7C76B6F81D2C44F30030FC98 /* String+SHA1.swift */,
@@ -647,6 +652,7 @@
 				269B47951D3AAAE20042D137 /* Files.swift in Sources */,
 				269B47961D3AAAE20042D137 /* HttpRouter.swift in Sources */,
 				269B47971D3AAAE20042D137 /* String+SHA1.swift in Sources */,
+				7C458EFE1D4A7526006A68E5 /* Socket+Server.swift in Sources */,
 				269B47981D3AAAE20042D137 /* Errno.swift in Sources */,
 				269B47991D3AAAE20042D137 /* String+BASE64.swift in Sources */,
 			);
@@ -672,6 +678,7 @@
 				7C76B7111D2C45710030FC98 /* Files.swift in Sources */,
 				7C76B7191D2C457C0030FC98 /* HttpRouter.swift in Sources */,
 				7C76B7291D2C45920030FC98 /* String+SHA1.swift in Sources */,
+				7C458EFC1D4A7526006A68E5 /* Socket+Server.swift in Sources */,
 				7C76B2A21D369C9D00D35BFB /* Errno.swift in Sources */,
 				7C76B7251D2C458C0030FC98 /* String+BASE64.swift in Sources */,
 			);
@@ -697,6 +704,7 @@
 				7C76B7121D2C45710030FC98 /* Files.swift in Sources */,
 				7C76B71A1D2C457C0030FC98 /* HttpRouter.swift in Sources */,
 				7C76B72A1D2C45920030FC98 /* String+SHA1.swift in Sources */,
+				7C458EFD1D4A7526006A68E5 /* Socket+Server.swift in Sources */,
 				7C76B2A31D369C9D00D35BFB /* Errno.swift in Sources */,
 				7C76B7261D2C458D0030FC98 /* String+BASE64.swift in Sources */,
 			);

+ 2 - 1
XCode/SwifterSampleOSX/main.swift

@@ -14,7 +14,8 @@ do {
     }
     
     try server.start(9080, forceIPv4: true)
-    print("Server has started ( port = \(server.port) ). Try to connect now...")
+    
+    print("Server has started ( port = \(try server.port()) ). Try to connect now...")
     
     NSRunLoop.mainRunLoop().run()