Created
April 11, 2016 03:28
-
-
Save nickmain/cf0b50e9fb14f36f86e459b77232989b to your computer and use it in GitHub Desktop.
Revisions
-
nickmain created this gist
Apr 11, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,125 @@ //: 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<FILE> 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<CChar>(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<CChar>(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") }