//: Playground - noun: a place where people can play import Foundation class ServerSocket { private var sockAddrLen = socklen_t(sizeof(sockaddr_in)) private var serverSocket = socket(AF_INET, SOCK_STREAM, 0) init?(_ port: UInt16) { var option: UInt32 = 1 setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, 4) var sockAddr = sockaddr_in() sockAddr.sin_family = sa_family_t(AF_INET) //IPv4 sockAddr.sin_port = CFSwapInt16(port) sockAddr.sin_addr = in_addr(s_addr: 0) //bind to any address guard (withUnsafePointer(&sockAddr) { bind(serverSocket, UnsafePointer($0), sockAddrLen) }) == 0 else { return nil } guard listen(serverSocket, 5 /*queue*/) == 0 else { return nil } } func waitForRequest() -> ClientRequest { var clientAddr = sockaddr_in() let incomingSocket = withUnsafeMutablePointer(&clientAddr) { accept(serverSocket, UnsafeMutablePointer($0), &sockAddrLen) } return ClientRequest(incomingSocket) } func closeSocket() { if serverSocket != 0 { close(serverSocket) serverSocket = 0 } } deinit { closeSocket() } } class ClientRequest { private var clientSocket: Int32 private var stream: UnsafeMutablePointer var method = "" var path = "" var headers: [String: String] = [:] var body = "" private init(_ socket: Int32) { clientSocket = socket stream = fdopen(socket, "r+") // open for reading+writing var buffer = Array(count: 1000, repeatedValue: 0) func readLine() -> String? { if fgets(&buffer, 1000, stream) != nil { var line = String.fromCString(&buffer)! line = line.substringToIndex(line.endIndex.advancedBy(-1)) //chop off the newline if !line.isEmpty { return line } } return nil } // read method and path guard let httpLine = readLine() else { return } let parts = httpLine.componentsSeparatedByString(" ") method = parts[0].uppercaseString path = parts[1] // read headers up to the empty line repeat { guard let line = readLine() else { break } let elems = line.characters.split(":", maxSplit: 1, allowEmptySlices: true) let name = String(elems[0]).lowercaseString let value = String(elems[1]).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) headers.updateValue(value, forKey: name) } while true // read body if let lengthValue = headers["content-length"], let length = Int(lengthValue) { var data = Array(count: length + 1, repeatedValue: 0) fread(&data, 1, length, stream) body = String.fromCString(&data)! } } // Respond with the given status string func respond(status: String) { write("HTTP/1.1 \(status)\r\n\r\n") } // Respond with the given status string and body func respond(status: String, content: String, type: String) { var body = content.cStringUsingEncoding(NSASCIIStringEncoding)! let length = strlen(&body) write("HTTP/1.1 \(status)\r\nContent-Type: \(type)\r\nContent-Length: \(length)\r\n\r\n") write(body) } private func write(text: String) { write(text.cStringUsingEncoding(NSASCIIStringEncoding)!) } private func write(chars: [CChar]) { var data = chars fwrite(&data, 1, size_t(strlen(&data)), stream) } deinit { fflush(stream) fclose(stream) close(clientSocket) } } if let server = ServerSocket(8080) { let req = server.waitForRequest() print(req.headers) req.respond("200 OK", content: "hello world", type: "text/plain") }