// // HttpServer.swift // Swifter // Copyright (c) 2015 Damian KoĊ‚akowski. All rights reserved. // import Foundation #if os(Linux) import Glibc import NSLinux #endif public class HttpServerIO { private var listenSocket: Socket = Socket(socketFileDescriptor: -1) private var clientSockets: Set = [] private let clientSocketsLock = NSLock() public func start(listenPort: in_port_t = 8080) throws { stop() listenSocket = try Socket.tcpSocketForListen(listenPort) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { while let socket = try? self.listenSocket.acceptClientSocket() { self.lock(self.clientSocketsLock) { self.clientSockets.insert(socket) } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), { self.handleConnection(socket) self.lock(self.clientSocketsLock) { self.clientSockets.remove(socket) } }) } self.stop() } } public func handleConnection(socket: Socket) { let address = try? socket.peername() let parser = HttpParser() while let request = try? parser.readHttpRequest(socket) { let request = request let (params, handler) = self.dispatch(request.method, path: request.path) request.address = address request.params = params; let response = handler(request) var keepConnection = parser.supportsKeepAlive(request.headers) do { keepConnection = try self.respond(socket, response: response, keepAlive: keepConnection) } catch { print("Failed to send response: \(error)") break } if !keepConnection { break } } socket.release() } public func dispatch(method: String, path: String) -> ([String: String], HttpRequest -> HttpResponse) { return ([:], { _ in HttpResponse.NotFound }) } public func stop() { listenSocket.release() lock(self.clientSocketsLock) { for socket in self.clientSockets { socket.shutdwn() } self.clientSockets.removeAll(keepCapacity: true) } } private func lock(handle: NSLock, closure: () -> ()) { handle.lock() closure() handle.unlock(); } private struct InnerWriteContext: HttpResponseBodyWriter { let socket: Socket func write(data: [UInt8]) { let _ = try? socket.writeUInt8(data) } } private func respond(socket: Socket, response: HttpResponse, keepAlive: Bool) throws -> Bool { try socket.writeUTF8("HTTP/1.1 \(response.statusCode()) \(response.reasonPhrase())\r\n") let content = response.content() if content.length >= 0 { try socket.writeUTF8("Content-Length: \(content.length)\r\n") } if keepAlive && content.length != -1 { try socket.writeUTF8("Connection: keep-alive\r\n") } for (name, value) in response.headers() { try socket.writeUTF8("\(name): \(value)\r\n") } try socket.writeUTF8("\r\n") if let writeClosure = content.writeClosure { let context = InnerWriteContext(socket: socket) try writeClosure(context) } return keepAlive && content.length != -1; } }