Procházet zdrojové kódy

Merged the last changes from stable branch.

Damian Kołakowski před 10 roky
rodič
revize
fab99390c2

+ 1 - 1
Sources/Swifter/App.swift

@@ -47,7 +47,7 @@ public class App {
         
         // Boot the server.
         
-        print("Starting Swifter (\(HttpServer.VERSION)) at port \(port) with PID \(Process.PID)...")
+        print("Starting Swifter (\(HttpServer.VERSION)) at port \(try server.port()) with PID \(Process.tid)...")
         
         try self.server.start(port)
         

+ 21 - 0
Sources/Swifter/Errno.swift

@@ -0,0 +1,21 @@
+//
+//  Errno.swift
+//  Swifter
+//
+//  Created by Damian Kolakowski on 13/07/16.
+//  Copyright © 2016 Damian Kołakowski. All rights reserved.
+//
+
+#if os(Linux)
+    import Glibc
+#else
+    import Foundation
+#endif
+
+
+public struct Errno {
+    
+    public static var description: String {
+        return String(cString: UnsafePointer(strerror(errno))) ?? "Error: \(errno)"
+    }
+}

+ 17 - 20
Sources/Swifter/File.swift

@@ -141,25 +141,22 @@ public class File {
     }
 }
 
-extension File {
-    
-    public static func withNewFileOpenedForWriting<Result>(_ path: String, _ f: (File) throws -> Result) throws -> Result {
-        return try withFileOpenedForMode(path, mode: "wb", f)
-    }
-    
-    public static func withFileOpenedForReading<Result>(_ path: String, _ f: (File) throws -> Result) throws -> Result {
-        return try withFileOpenedForMode(path, mode: "rb", f)
-    }
-    
-    public static func withFileOpenedForWritingAndReading<Result>(_ path: String, _ f: (File) throws -> Result) throws -> Result {
-        return try withFileOpenedForMode(path, mode: "r+b", f)
-    }
-    
-    public static func withFileOpenedForMode<Result>(_ path: String, mode: String, _ f: (File) throws -> Result) throws -> Result {
-        let file = try File.openFileForMode(path, mode)
-        defer {
-            file.close()
-        }
-        return try f(file)
+public func withNewFileOpenedForWriting<Result>(path: String, _ f: (File) throws -> Result) throws -> Result {
+    return try withFileOpenedForMode(path, "wb", f)
+}
+
+public func withFileOpenedForReading<Result>(path: String, _ f: (File) throws -> Result) throws -> Result {
+    return try withFileOpenedForMode(path, "rb", f)
+}
+
+public func withFileOpenedForWritingAndReading<Result>(path: String, _ f: (File) throws -> Result) throws -> Result {
+    return try withFileOpenedForMode(path, "r+b", f)
+}
+
+public func withFileOpenedForMode<Result>(_ path: String, _ mode: String, _ f: (File) throws -> Result) throws -> Result {
+    let file = try File.openFileForMode(path, mode)
+    defer {
+        file.close()
     }
+    return try f(file)
 }

+ 3 - 6
Sources/Swifter/HttpParser.swift

@@ -57,16 +57,13 @@ public class HttpParser {
     
     private func readHeaders(_ socket: Socket) throws -> [String: String] {
         var headers = [String: String]()
-        repeat {
-            let headerLine = try socket.readLine()
-            if headerLine.isEmpty {
-                return headers
-            }
+        while case let headerLine = try socket.readLine(), !headerLine.isEmpty {
             let headerTokens = headerLine.split(1, separator: ":")
             if let name = headerTokens.first, let value = headerTokens.last {
                 headers[name.lowercased()] = value.trim()
             }
-        } while true
+        }
+        return headers
     }
     
     func supportsKeepAlive(_ headers: [String: String]) -> Bool {

+ 9 - 9
Sources/Swifter/HttpResponse.swift

@@ -13,9 +13,9 @@ public enum SerializationError: Error {
 }
 
 public protocol HttpResponseBodyWriter {
-    func write(_ file: File)
-    func write(_ data: [UInt8])
-    func write(_ data: ArraySlice<UInt8>)
+    func write(_ file: File) throws
+    func write(_ data: [UInt8]) throws
+    func write(_ data: ArraySlice<UInt8>) throws
 }
 
 public enum HttpResponseBody {
@@ -44,35 +44,35 @@ public enum HttpResponseBody {
                         return Array(UnsafeBufferPointer(start: body, count: json.count))
                     })
                     return (data.count, {
-                        $0.write(data)
+                        try $0.write(data)
                     })
                 #endif
             case .text(let body):
                 let data = [UInt8](body.utf8)
                 return (data.count, {
-                    $0.write(data)
+                    try $0.write(data)
                 })
             case .html(let body):
                 let serialised = "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
                 let data = [UInt8](serialised.utf8)
                 return (data.count, {
-                    $0.write(data)
+                    try $0.write(data)
                 })
             case .data(let body):
                 return (body.count, {
-                    $0.write(body)
+                    try $0.write(body)
                 })
             case .custom(let object, let closure):
                 let serialised = try closure(object)
                 let data = [UInt8](serialised.utf8)
                 return (data.count, {
-                    $0.write(data)
+                    try $0.write(data)
                 })
             }
         } catch {
             let data = [UInt8]("Serialisation error: \(error)".utf8)
             return (data.count, {
-                $0.write(data)
+                try $0.write(data)
             })
         }
     }

+ 8 - 0
Sources/Swifter/HttpServer.swift

@@ -20,9 +20,17 @@ public class HttpServer: HttpServerIO {
         self.POST   = MethodRoute(method: "POST", router: router)
         self.GET    = MethodRoute(method: "GET", router: router)
         self.PUT    = MethodRoute(method: "PUT", router: router)
+        
+        self.delete = MethodRoute(method: "DELETE", router: router)
+        self.update = MethodRoute(method: "UPDATE", router: router)
+        self.head   = MethodRoute(method: "HEAD", router: router)
+        self.post   = MethodRoute(method: "POST", router: router)
+        self.get    = MethodRoute(method: "GET", router: router)
+        self.put    = MethodRoute(method: "PUT", router: router)
     }
     
     public var DELETE, UPDATE, HEAD, POST, GET, PUT : MethodRoute
+    public var delete, update, head, post, get, put : MethodRoute
     
     public func get(_ path: String, _ handler: ((HttpRequest) -> HttpResponse)) {
         router.register("GET", path: path, handler: handler)

+ 35 - 76
Sources/Swifter/HttpServerIO.swift

@@ -14,38 +14,49 @@
 
 public class HttpServerIO {
     
-    private var listenSocket: Socket = Socket(socketFileDescriptor: -1)
-    private var clientSockets: Set<Socket> = []
-    private let clientSocketsLock = NSLock()
+    private var socket = Socket(socketFileDescriptor: -1)
+    private var sockets = Set<Socket>()
+    
+    public private(set) var running = false
+    
+    public func port() throws -> Int {
+        return Int(try socket.port())
+    }
+    
+    public func isIPv4() throws -> Bool {
+        return try socket.isIPv4()
+    }
+    
+    deinit {
+        stop()
+    }
     
     @available(OSX 10.10, *)
     public func start(_ listenPort: in_port_t = 8080, forceIPv4: Bool = false) throws {
         stop()
-        listenSocket = try Socket.tcpSocketForListen(listenPort, forceIPv4: forceIPv4)
+        socket = try Socket.tcpSocketForListen(listenPort, forceIPv4)
+        self.running = true
         DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
-            while let socket = try? self.listenSocket.acceptClientSocket() {
-                self.lock(self.clientSocketsLock) {
-                    self.clientSockets.insert(socket)
-                }
+            while let socket = try? self.socket.acceptClientSocket() {
                 DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
+                    self.sockets.insert(socket)
                     self.handleConnection(socket)
-                    self.lock(self.clientSocketsLock) {
-                        self.clientSockets.remove(socket)
-                    }
+                    self.sockets.remove(socket)
                 }
             }
             self.stop()
+            self.running = false
         }
     }
     
     public func stop() {
-        listenSocket.release()
-        lock(self.clientSocketsLock) {
-            for socket in self.clientSockets {
-                socket.shutdwn()
-            }
-            self.clientSockets.removeAll(keepingCapacity: true)
+        // Shutdown connected peers because they can live in 'keep-alive' or 'websocket' loops.
+        for socket in self.sockets {
+            socket.shutdwn()
         }
+        self.sockets.removeAll(keepingCapacity: true)
+        socket.release()
+        self.running = false
     }
     
     public func dispatch(_ request: HttpRequest) -> ([String: String], (HttpRequest) -> HttpResponse) {
@@ -53,13 +64,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 = try? socket.peername()
             let (params, handler) = self.dispatch(request)
-            request.address = address
-            request.params = params;
+            request.params = params
             let response = handler(request)
             var keepConnection = parser.supportsKeepAlive(request.headers)
             do {
@@ -77,31 +87,20 @@ 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) {
+        func write(_ file: File) throws {
             var offset: off_t = 0
-            
             let _ = sendfile(fileno(file.pointer), socket.socketFileDescriptor, 0, &offset, nil, 0)
         }
         
-        func write(_ data: [UInt8]) {
-            write(ArraySlice(data))
+        func write(_ data: [UInt8]) throws {
+            try write(ArraySlice(data))
         }
         
-        func write(_ data: ArraySlice<UInt8>) {
-            do {
-                try socket.writeUInt8(data)
-            } catch {
-                print("\(error)")
-            }
+        func write(_ data: ArraySlice<UInt8>) throws {
+            try socket.writeUInt8(data)
         }
     }
     
@@ -134,46 +133,6 @@ public class HttpServerIO {
 }
 
 #if os(Linux)
-    
-import Glibc
-    
-struct sf_hdtr { }
-    
-// Linux supports sendfile (http://man7.org/linux/man-pages/man2/sendfile.2.html)
-// but it's not exposed by the module map from the Swift toolchain.
-//
-// TODO - use @_silgen_name to get the sendfile entry point.
-    
-func sendfile(_ source: Int32, _ target: Int32, _: off_t, _: UnsafeMutablePointer<off_t>!, _: UnsafeMutablePointer<sf_hdtr>!, _: Int32) -> Int32 {
-    var buffer = [UInt8](repeating: 0, count: 1024)
-    while true {
-        let readResult = read(source, &buffer, buffer.count)
-        guard readResult > 0 else {
-            return Int32(readResult)
-        }
-        var writeCounter = 0
-        while writeCounter < readResult {
-            let writeResult = write(target, &buffer + writeCounter, readResult - writeCounter)
-            guard writeResult > 0 else {
-                return Int32(writeResult)
-            }
-            writeCounter = writeCounter + writeResult
-        }
-    }
-}
-
-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) }
-}
 
 public class DispatchQueue {
     

+ 2 - 2
Sources/Swifter/Process.swift

@@ -9,11 +9,11 @@ import Foundation
 
 public class Process {
     
-    public static var PID: Int {
+    public static var pid: Int {
         return Int(getpid())
     }
     
-    public static var TID: UInt64 {
+    public static var tid: UInt64 {
         var tid: __uint64_t = 0
         pthread_threadid_np(nil, &tid);
         return UInt64(tid)

+ 8 - 8
Sources/Swifter/Scopes.swift

@@ -13,10 +13,10 @@
 
 public func scopes(_ scope: Closure) -> ((HttpRequest) -> HttpResponse) {
     return { r in
-        ScopesBuffer[Process.TID] = ""
+        ScopesBuffer[Process.tid] = ""
         scope()
         return .raw(200, "OK", ["Content-Type": "text/html"],
-                    { $0.write([UInt8](("<!DOCTYPE html>"  + (ScopesBuffer[Process.TID] ?? "")).utf8)) })
+                    { $0.write([UInt8](("<!DOCTYPE html>"  + (ScopesBuffer[Process.tid] ?? "")).utf8)) })
     }
 }
 
@@ -586,15 +586,15 @@ private func evaluate(_ node: String, _ attrs: [String: String?] = [:], _ c: Clo
     acceptCharset = nil
     inner = nil
     
-    ScopesBuffer[Process.TID] = (ScopesBuffer[Process.TID] ?? "") + "<" + node
+    ScopesBuffer[Process.tid] = (ScopesBuffer[Process.tid] ?? "") + "<" + node
     
     // Save the current output before the nested scope evalutation.
     
-    var output = ScopesBuffer[Process.TID] ?? ""
+    var output = ScopesBuffer[Process.tid] ?? ""
     
     // Clear the output buffer for the evalutation.
     
-    ScopesBuffer[Process.TID] = ""
+    ScopesBuffer[Process.tid] = ""
     
     // Evaluate the nested scope.
     
@@ -739,10 +739,10 @@ private func evaluate(_ node: String, _ attrs: [String: String?] = [:], _ c: Clo
     }
     
     if let inner = inner {
-        ScopesBuffer[Process.TID] = output + ">" + (inner) + "</" + node + ">"
+        ScopesBuffer[Process.tid] = output + ">" + (inner) + "</" + node + ">"
     } else {
-        let current = ScopesBuffer[Process.TID]  ?? ""
-        ScopesBuffer[Process.TID] = output + ">" + current + "</" + node + ">"
+        let current = ScopesBuffer[Process.tid]  ?? ""
+        ScopesBuffer[Process.tid] = output + ">" + current + "</" + node + ">"
     }
     
     // Pop the attributes.

+ 53 - 0
Sources/Swifter/Socket+File.swift

@@ -0,0 +1,53 @@
+//
+//  Socket+File.swift
+//  Swifter
+//
+//  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
+//
+
+#if os(Linux)
+    import Glibc
+#else
+    import Foundation
+#endif
+
+
+#if os(iOS) || os (Linux)
+    
+    struct sf_hdtr { }
+    
+    private func sendfileImpl(source: Int32, _ target: Int32, _: off_t, _: UnsafeMutablePointer<off_t>, _: UnsafeMutablePointer<sf_hdtr>, _: Int32) -> Int32 {
+        var buffer = [UInt8](count: 1024, repeatedValue: 0)
+        while true {
+            let readResult = read(source, &buffer, buffer.count)
+            guard readResult > 0 else {
+                return Int32(readResult)
+            }
+            var writeCounter = 0
+            while writeCounter < readResult {
+                let writeResult = write(target, &buffer + writeCounter, readResult - writeCounter)
+                guard writeResult > 0 else {
+                    return Int32(writeResult)
+                }
+                writeCounter = writeCounter + writeResult
+            }
+        }
+    }
+    
+#else
+    
+    private let sendfileImpl = sendfile
+    
+#endif
+
+extension Socket {
+    
+    public func writeFile(file: File) throws -> Void {
+        var offset: off_t = 0
+        let result = sendfileImpl(fileno(file.pointer), self.socketFileDescriptor, 0, &offset, nil, 0)
+        if result == -1 {
+            throw SocketError.writeFailed("sendfile: " + Errno.description)
+        }
+    }
+    
+}

+ 108 - 0
Sources/Swifter/Socket+Server.swift

@@ -0,0 +1,108 @@
+//
+//  Socket+Server.swift
+//  Swifter
+//
+//  Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
+//
+
+#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.self))) == -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.self)),
+                                       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.self)))
+                }
+            } else {
+                var addr = sockaddr_in6(sin6_len: UInt8(strideof(sockaddr_in6.self)),
+                                        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.self)))
+                }
+            }
+        #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)
+    }
+    
+    public func acceptClientSocket() throws -> Socket {
+        var addr = sockaddr()
+        var len: socklen_t = 0
+        let clientSocket = accept(self.socketFileDescriptor, &addr, &len)
+        if clientSocket == -1 {
+            throw SocketError.acceptFailed(Errno.description)
+        }
+        Socket.setNoSigPipe(clientSocket)
+        return Socket(socketFileDescriptor: clientSocket)
+    }
+}

+ 36 - 105
Sources/Swifter/Socket.swift

@@ -24,92 +24,21 @@ public enum SocketError: Error {
     case getNameInfoFailed(String)
     case acceptFailed(String)
     case recvFailed(String)
+    case getSockNameFailed(String)
 }
 
 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(Socket.descriptionOfLastError())
-        }
-        
-        var value: Int32 = 1
-        if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &value, socklen_t(sizeof(Int32.self))) == -1 {
-            let details = Socket.descriptionOfLastError()
-            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: Socket.htonsPort(port),
-                    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: Socket.htonsPort(port),
-                    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.self)),
-                    sin_family: UInt8(AF_INET),
-                    sin_port: Socket.htonsPort(port),
-                    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.self))) }
-            } else {
-                // “Apple recommends always making an IPv6 socket to listen on.  The OS will automatically
-                // “downgrade” it to an IPv4 socket if necessary, so there is no need to listen on two different sockets”.
-                var addr = sockaddr_in6(sin6_len: UInt8(strideof(sockaddr_in6.self)),
-                    sin6_family: UInt8(AF_INET6),
-                    sin6_port: Socket.htonsPort(port),
-                    sin6_flowinfo: 0,
-                    sin6_addr: in6addr_any,
-                    sin6_scope_id: 0)
-                
-                bindResult = withUnsafePointer(&addr) { bind(socketFileDescriptor, UnsafePointer<sockaddr>($0), socklen_t(sizeof(sockaddr_in6.self))) }
-            }
-        #endif
-
-        if bindResult == -1 {
-            let details = Socket.descriptionOfLastError()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.bindFailed(details)
-        }
-        
-        if listen(socketFileDescriptor, maxPendingConnection ) == -1 {
-            let details = Socket.descriptionOfLastError()
-            Socket.release(socketFileDescriptor)
-            throw SocketError.listenFailed(details)
-        }
-        return Socket(socketFileDescriptor: socketFileDescriptor)
-    }
-    
-    internal let socketFileDescriptor: Int32
+    let socketFileDescriptor: Int32
     
     public init(socketFileDescriptor: Int32) {
         self.socketFileDescriptor = socketFileDescriptor
     }
     
+    deinit {
+        shutdwn()
+    }
+    
     public var hashValue: Int { return Int(self.socketFileDescriptor) }
     
     public func release() {
@@ -120,15 +49,30 @@ public class Socket: Hashable, Equatable {
         Socket.shutdwn(self.socketFileDescriptor)
     }
     
-    public func acceptClientSocket() throws -> Socket {
-        var addr = sockaddr()        
-        var len: socklen_t = 0
-        let clientSocket = accept(self.socketFileDescriptor, &addr, &len)
-        if clientSocket == -1 {
-            throw SocketError.acceptFailed(Socket.descriptionOfLastError())
+    public func port() throws -> in_port_t {
+        var addr = sockaddr_in()
+        return try withUnsafePointer(&addr) { pointer in
+            var len = socklen_t(sizeof(sockaddr_in.self))
+            if getsockname(socketFileDescriptor, UnsafeMutablePointer(pointer), &len) != 0 {
+                throw SocketError.getSockNameFailed(Errno.description)
+            }
+            #if os(Linux)
+                return ntohs(addr.sin_port)
+            #else
+                return Int(OSHostByteOrder()) != OSLittleEndian ? addr.sin_port.littleEndian : addr.sin_port.bigEndian
+            #endif
+        }
+    }
+    
+    public func isIPv4() throws -> Bool {
+        var addr = sockaddr_in()
+        return try withUnsafePointer(&addr) { pointer in
+            var len = socklen_t(sizeof(sockaddr_in.self))
+            if getsockname(socketFileDescriptor, UnsafeMutablePointer(pointer), &len) != 0 {
+                throw SocketError.getSockNameFailed(Errno.description)
+            }
+            return Int32(addr.sin_family) == AF_INET
         }
-        Socket.setNoSigPipe(clientSocket)
-        return Socket(socketFileDescriptor: clientSocket)
     }
     
     public func writeUTF8(_ string: String) throws {
@@ -152,7 +96,7 @@ public class Socket: Hashable, Equatable {
                     let s = write(self.socketFileDescriptor, baseAddress + sent, Int(data.count - sent))
                 #endif
                 if s <= 0 {
-                    throw SocketError.writeFailed(Socket.descriptionOfLastError())
+                    throw SocketError.writeFailed(Errno.description)
                 }
                 sent += s
             }
@@ -163,7 +107,7 @@ public class Socket: Hashable, Equatable {
         var buffer = [UInt8](repeating: 0, count: 1)
         let next = recv(self.socketFileDescriptor as Int32, &buffer, Int(buffer.count), 0)
         if next <= 0 {
-            throw SocketError.recvFailed(Socket.descriptionOfLastError())
+            throw SocketError.recvFailed(Errno.description)
         }
         return buffer[0]
     }
@@ -184,20 +128,16 @@ public class Socket: Hashable, Equatable {
     public func peername() throws -> String {
         var addr = sockaddr(), len: socklen_t = socklen_t(sizeof(sockaddr.self))
         if getpeername(self.socketFileDescriptor, &addr, &len) != 0 {
-            throw SocketError.getPeerNameFailed(Socket.descriptionOfLastError())
+            throw SocketError.getPeerNameFailed(Errno.description)
         }
         var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
         if getnameinfo(&addr, len, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST) != 0 {
-            throw SocketError.getNameInfoFailed(Socket.descriptionOfLastError())
+            throw SocketError.getNameInfoFailed(Errno.description)
         }
         return String(cString: hostBuffer)
     }
     
-    private class func descriptionOfLastError() -> String {
-        return String(cString: UnsafePointer(strerror(errno))) ?? "Error: \(errno)"
-    }
-    
-    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.
@@ -208,7 +148,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
@@ -216,7 +156,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))
             close(socket)
@@ -228,15 +168,6 @@ public class Socket: Hashable, Equatable {
             }
         #endif
     }
-    
-    private class func htonsPort(_ port: in_port_t) -> in_port_t {
-        #if os(Linux)
-            return htons(port)
-        #else
-            let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian
-            return isLittleEndian ? _OSSwapInt16(port) : port
-        #endif
-    }
 }
 
 public func ==(socket1: Socket, socket2: Socket) -> Bool {

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

@@ -93,6 +93,15 @@
 		7C5F78FE1D5520B000C514AA /* RSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F78FB1D5520B000C514AA /* RSA.swift */; };
 		7C5F79041D55F44500C514AA /* SwifterTestsRSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F79031D55F44500C514AA /* SwifterTestsRSA.swift */; };
 		7C5F79051D55F44500C514AA /* SwifterTestsRSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F79031D55F44500C514AA /* SwifterTestsRSA.swift */; };
+		7C5F79071D5627EE00C514AA /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F79061D5627EE00C514AA /* Socket+Server.swift */; };
+		7C5F79081D5627EE00C514AA /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F79061D5627EE00C514AA /* Socket+Server.swift */; };
+		7C5F79091D5627EE00C514AA /* Socket+Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F79061D5627EE00C514AA /* Socket+Server.swift */; };
+		7C5F790B1D56281200C514AA /* Errno.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F790A1D56281200C514AA /* Errno.swift */; };
+		7C5F790C1D56281200C514AA /* Errno.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F790A1D56281200C514AA /* Errno.swift */; };
+		7C5F790D1D56281200C514AA /* Errno.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F790A1D56281200C514AA /* Errno.swift */; };
+		7C5F790F1D56287000C514AA /* Socket+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F790E1D56287000C514AA /* Socket+File.swift */; };
+		7C5F79101D56287000C514AA /* Socket+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F790E1D56287000C514AA /* Socket+File.swift */; };
+		7C5F79111D56287000C514AA /* Socket+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5F790E1D56287000C514AA /* Socket+File.swift */; };
 		7C6B57EB1CA6C3AA0042655C /* SwifterTestsHttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6B57EA1CA6C3AA0042655C /* SwifterTestsHttpRouter.swift */; };
 		7C6B57EC1CA6C3AA0042655C /* SwifterTestsHttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6B57EA1CA6C3AA0042655C /* SwifterTestsHttpRouter.swift */; };
 		7C71C5B01A1D52F800682BF0 /* login.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 98630C061A1C9A9D00478D08 /* login.html */; };
@@ -225,6 +234,9 @@
 		7C5F78F51D54D21600C514AA /* SwifterTestsRC4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsRC4.swift; sourceTree = "<group>"; };
 		7C5F78FB1D5520B000C514AA /* RSA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSA.swift; sourceTree = "<group>"; };
 		7C5F79031D55F44500C514AA /* SwifterTestsRSA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsRSA.swift; sourceTree = "<group>"; };
+		7C5F79061D5627EE00C514AA /* Socket+Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Socket+Server.swift"; sourceTree = "<group>"; };
+		7C5F790A1D56281200C514AA /* Errno.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errno.swift; sourceTree = "<group>"; };
+		7C5F790E1D56287000C514AA /* Socket+File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Socket+File.swift"; sourceTree = "<group>"; };
 		7C6B57EA1CA6C3AA0042655C /* SwifterTestsHttpRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwifterTestsHttpRouter.swift; sourceTree = "<group>"; };
 		7C7488771C1DA07300CBCD77 /* file.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = file.html; sourceTree = "<group>"; };
 		7C839B6E19422CFF003A6950 /* SwifterSampleiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwifterSampleiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -383,6 +395,8 @@
 				7C3195F31CC2C68F00DF5406 /* Process.swift */,
 				7C3195F41CC2C68F00DF5406 /* Reflection.swift */,
 				7C3195F51CC2C68F00DF5406 /* Socket.swift */,
+				7C5F79061D5627EE00C514AA /* Socket+Server.swift */,
+				7C5F790E1D56287000C514AA /* Socket+File.swift */,
 				7C3195F71CC2C68F00DF5406 /* SQLite.swift */,
 				7C3195F81CC2C68F00DF5406 /* String+BASE64.swift */,
 				7C3195F91CC2C68F00DF5406 /* String+Misc.swift */,
@@ -391,6 +405,7 @@
 				7C1145981D527545000DB965 /* AES128.swift */,
 				7C5F78F11D54C99200C514AA /* RC4.swift */,
 				7C5F78FB1D5520B000C514AA /* RSA.swift */,
+				7C5F790A1D56281200C514AA /* Errno.swift */,
 			);
 			path = Swifter;
 			sourceTree = "<group>";
@@ -829,6 +844,7 @@
 				7C31962B1CC2C68F00DF5406 /* Socket.swift in Sources */,
 				7C5F78FC1D5520B000C514AA /* RSA.swift in Sources */,
 				7C3196191CC2C68F00DF5406 /* HttpResponse.swift in Sources */,
+				7C5F790F1D56287000C514AA /* Socket+File.swift in Sources */,
 				7C3196131CC2C68F00DF5406 /* HttpParser.swift in Sources */,
 				7C31963A1CC2C68F00DF5406 /* String+SHA1.swift in Sources */,
 				7C31961F1CC2C68F00DF5406 /* HttpServer.swift in Sources */,
@@ -843,9 +859,11 @@
 				7C3196281CC2C68F00DF5406 /* Reflection.swift in Sources */,
 				7C3945641D256FDA003EEABA /* Scopes.swift in Sources */,
 				7C3196161CC2C68F00DF5406 /* HttpRequest.swift in Sources */,
+				7C5F79071D5627EE00C514AA /* Socket+Server.swift in Sources */,
 				7C3196221CC2C68F00DF5406 /* HttpServerIO.swift in Sources */,
 				7C31960A1CC2C68F00DF5406 /* Files.swift in Sources */,
 				7CB923CC1D50F11700899E2A /* JSON.swift in Sources */,
+				7C5F790B1D56281200C514AA /* Errno.swift in Sources */,
 				7C3196011CC2C68F00DF5406 /* App.swift in Sources */,
 				7C3196251CC2C68F00DF5406 /* Process.swift in Sources */,
 				7C3196371CC2C68F00DF5406 /* String+Misc.swift in Sources */,
@@ -860,6 +878,7 @@
 				7C31962C1CC2C68F00DF5406 /* Socket.swift in Sources */,
 				7C5F78FD1D5520B000C514AA /* RSA.swift in Sources */,
 				7C31961A1CC2C68F00DF5406 /* HttpResponse.swift in Sources */,
+				7C5F79101D56287000C514AA /* Socket+File.swift in Sources */,
 				7C3196141CC2C68F00DF5406 /* HttpParser.swift in Sources */,
 				7C31963B1CC2C68F00DF5406 /* String+SHA1.swift in Sources */,
 				7C3196201CC2C68F00DF5406 /* HttpServer.swift in Sources */,
@@ -874,9 +893,11 @@
 				7C3196291CC2C68F00DF5406 /* Reflection.swift in Sources */,
 				7C3945651D256FDA003EEABA /* Scopes.swift in Sources */,
 				7C3196171CC2C68F00DF5406 /* HttpRequest.swift in Sources */,
+				7C5F79081D5627EE00C514AA /* Socket+Server.swift in Sources */,
 				7C3196231CC2C68F00DF5406 /* HttpServerIO.swift in Sources */,
 				7C31960B1CC2C68F00DF5406 /* Files.swift in Sources */,
 				7CB923CD1D50F11800899E2A /* JSON.swift in Sources */,
+				7C5F790C1D56281200C514AA /* Errno.swift in Sources */,
 				7C3196021CC2C68F00DF5406 /* App.swift in Sources */,
 				7C3196261CC2C68F00DF5406 /* Process.swift in Sources */,
 				7C3196381CC2C68F00DF5406 /* String+Misc.swift in Sources */,
@@ -917,6 +938,7 @@
 				7C31962D1CC2C68F00DF5406 /* Socket.swift in Sources */,
 				7C5F78FE1D5520B000C514AA /* RSA.swift in Sources */,
 				7C31961B1CC2C68F00DF5406 /* HttpResponse.swift in Sources */,
+				7C5F79111D56287000C514AA /* Socket+File.swift in Sources */,
 				7C3196151CC2C68F00DF5406 /* HttpParser.swift in Sources */,
 				7C31963C1CC2C68F00DF5406 /* String+SHA1.swift in Sources */,
 				7C83177B1D2C5DF300630662 /* Scopes.swift in Sources */,
@@ -931,9 +953,11 @@
 				7C3196061CC2C68F00DF5406 /* DemoServer.swift in Sources */,
 				7C31962A1CC2C68F00DF5406 /* Reflection.swift in Sources */,
 				7C3196181CC2C68F00DF5406 /* HttpRequest.swift in Sources */,
+				7C5F79091D5627EE00C514AA /* Socket+Server.swift in Sources */,
 				7C3196241CC2C68F00DF5406 /* HttpServerIO.swift in Sources */,
 				7C31960C1CC2C68F00DF5406 /* Files.swift in Sources */,
 				7CB923CE1D50F11800899E2A /* JSON.swift in Sources */,
+				7C5F790D1D56281200C514AA /* Errno.swift in Sources */,
 				7C3196031CC2C68F00DF5406 /* App.swift in Sources */,
 				7C3196271CC2C68F00DF5406 /* Process.swift in Sources */,
 				7C3196391CC2C68F00DF5406 /* String+Misc.swift in Sources */,

+ 0 - 9
XCode/SwifterSampleOSX/main.swift

@@ -7,15 +7,6 @@
 import Foundation
 import Swifter
 
-
-let encyrpted = RC4.encrypt([UInt8]("Plaintext".utf8), [UInt8]("Key".utf8))
-
-print(encyrpted.map({ String(format: "%02x", $0) }).joined(separator: ","))
-
-let deencyrpted = RC4.encrypt(encyrpted, [UInt8]("Key".utf8))
-
-print(deencyrpted.map({ String(format: "%02x", $0) }).joined(separator: ","))
-
 do {
     let server: HttpServer = demoServer(try File.currentWorkingDirectory())
     server["/testAfterBaseRoute"] = { request in