Skip to content

Instantly share code, notes, and snippets.

@brendanberg
Created June 9, 2014 23:26
Show Gist options
  • Select an option

  • Save brendanberg/eb10bda0d24d01606d4c to your computer and use it in GitHub Desktop.

Select an option

Save brendanberg/eb10bda0d24d01606d4c to your computer and use it in GitHub Desktop.

Revisions

  1. brendanberg created this gist Jun 9, 2014.
    17 changes: 17 additions & 0 deletions main.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    println("Hello")

    var conn = Socket().connect(8000, address: "127.0.0.1").listen(limit:128).accept() { connection, address in
    println("Accepted a connection from port \(address.addressString)")
    return connection
    }.read() { connection, inStr in
    let arr = inStr.componentsSeparatedByString("\n")
    let response = (arr[0].uppercaseString + "\n")
    return connection.write(response)
    }.close()

    switch conn {
    case .Error(let str):
    println("ERROR: \(str)")
    case .Descriptor:
    println("Goodbye")
    }
    318 changes: 318 additions & 0 deletions socket.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,318 @@
    // Created by Brendan Berg on 6/4/14.
    // Copyright (c) 2014 NegativeZero. All rights reserved.

    import Darwin

    /**
    Representation of a connected socket or error.
    Either a Descriptor with an associated value for the POSIX
    socket file descriptor or an Error with a string describing
    the error.
    */
    enum Connection {
    case Descriptor(CInt)
    case Error(String)
    }

    /**
    Representation of a socket or error.
    Either a Descriptor with an associated value for the POSIX
    socket file descriptor or an Error with a string describing
    the error.
    */
    enum Socket {
    case Descriptor(CInt)
    case Error(String)

    /**
    Creates a new, unbound POSIX socket and encapsulates the file descriptor.
    */
    init() {
    let newSocket = Darwin.socket(AF_INET, SOCK_STREAM, 0)

    if newSocket < 0 {
    self = .Error(String.fromCString(strerror(errno)))
    } else {
    self = .Descriptor(newSocket)
    }
    }

    /**
    Binds a socket to the specified port, listening on the interface specified by `address`.
    */
    func connect(port: CUnsignedShort, address: CString = "127.0.0.1", fn: Socket -> Socket = { $0 }) -> Socket {
    switch self {
    case let .Descriptor(sock):
    let addr = inet_addr(address)

    if addr == __uint32_t.max {
    return .Error("unable to parse address")
    }

    var server_addr = sockaddr()
    server_addr.sa_family = sa_family_t(AF_INET)
    server_addr.sin_port = port
    server_addr.sin_addr = addr

    let err = bind(sock, &server_addr, socklen_t(sizeof(sockaddr)))

    if err != 0 {
    return .Error(String.fromCString(strerror(errno)))
    }

    return fn(self)
    case .Error:
    return self
    }
    }

    /**
    Listens for connections on the socket, pulling from a queue with
    a maximum length specified by `limit`. Per the man page, `limit`
    is silently limited to 128.
    */
    func listen(limit: CInt = 128, fn: Socket -> Socket = { $0 }) -> Socket {
    switch self {
    case let .Descriptor(sock):
    let success = Darwin.listen(sock, limit)

    if success != 0 {
    return .Error(String.fromCString(strerror(errno)))
    } else {
    return self
    }
    case .Error:
    return self
    }
    }

    /**
    Accepts a connection on a socket. Turns a Socket into a Connection.
    */
    func accept(fn: (Connection, sockaddr) -> Connection = { c, _ in c }) -> Connection {
    switch self {
    case .Descriptor(let sock):
    var address = sockaddr()
    var length = socklen_t(sizeof(sockaddr))
    let newSocket = Darwin.accept(sock, &address, &length)

    if newSocket < 0 {
    return .Error(String.fromCString(strerror(errno)))
    } else {
    return fn(.Descriptor(newSocket), address)
    }
    case .Error(let str):
    return .Error(str) // self
    }
    }

    /**
    Closes a connection.
    */
    func close(fn: Socket -> Socket = { $0 }) -> Socket {
    switch self {
    case let .Descriptor(sock):
    if Darwin.close(sock) != 0 {
    return .Error(String.fromCString(strerror(errno)))
    } else {
    return fn(self)
    }
    case .Error:
    return self
    }
    }
    }



    extension Socket: LogicValue {
    /**
    Gets the logical value of the socket.

    Returns `true` if the Socket represents a valid descriptor
    and `false` if the Socket represents an error.
    */
    func getLogicValue() -> Bool {
    switch self {
    case .Descriptor:
    return true
    case .Error:
    return false
    }
    }
    }



    // Connection Extensions
    // ---------------------
    // Methods on the Connection enum are here because of the forward declaration
    // required for the Socket implementation.

    extension Connection {
    /**
    Reads data from the connection. The data read from the connection is passed as
    the second parameter to the success function.

    The `success` parameter is a function to be called upon successful reading.

    Returns a `Connection` monad
    */
    func read(fn: (Connection, String) -> Connection = { c, _ in c }) -> Connection {

    // Use this to quickly zero out memory if we reuse the same buffer per read
    // memset(&array, CInt(sizeof(CChar)) * CInt(array.count), 0)
    //
    // This is less Swiftish but might be faster...
    //
    // var buff = calloc(UInt(sizeof(CChar)), 256)
    // var n = read(conn, buff, 255)
    // var charBuf = UnsafePointer<CChar>(buff)
    // charBuf[n] = 0
    // var s: String = String.fromCString(charBuf)
    // println(s)

    switch self {
    case .Descriptor(let sock):
    var buffer = new CChar[256]
    let bytesRead = Darwin.read(sock, &buffer, UInt(buffer.count))

    if bytesRead < 0 {
    return .Error(String.fromCString(strerror(errno)))
    } else {
    buffer[bytesRead] = 0
    return fn(self, buffer.withUnsafePointerToElements { String.fromCString($0) })
    }
    case .Error:
    return self
    }
    }

    /**
    Writes the contents of a string to the connection.

    The `response` parameter is the string to be written; `success`
    is the function to be called upon successful writing.

    Returns a `Connection` monad.
    */
    func write(response: String, fn: Connection -> Connection = { $0 }) -> Connection {
    switch self {
    case .Descriptor(let sock):
    let bytesOut = UInt8[](response.utf8)
    let bytesWritten = Darwin.write(sock, bytesOut, UInt(bytesOut.count))

    if bytesWritten < 0 {
    return .Error(String.fromCString(strerror(errno)))
    } else {
    return fn(self)
    }
    case .Error:
    return self
    }
    }

    /**
    Closes the connection.

    The `success` parameter is a function to be called upon successful closure of the connection.

    Returns A `Connection` monad.
    */
    func close(fn: Connection -> Connection = { $0 }) -> Connection {
    switch self {
    case let .Descriptor(sock):
    if Darwin.close(sock) != 0 {
    return .Error(String.fromCString(strerror(errno)))
    } else {
    return fn(self)
    }
    case .Error:
    return self
    }
    }
    }



    extension Connection: LogicValue {
    func getLogicValue() -> Bool {
    /**
    Gets the logical value of the connection.

    Returns `true` if the Connection represents a valid descriptor,
    and `false` if the Connection represents an error
    */
    switch self {
    case .Descriptor:
    return true
    case .Error:
    return false
    }
    }
    }



    // C sockaddr struct Extension
    // ---------------------------
    // The Swift type checker doesn't allow us to use sockaddr and sockaddr_in
    // interchangably, so the following extension destructures port and address
    // types and sets the appropriate bytes in sa_data to use with the socket
    // system calls.

    extension sockaddr {
    init () {
    sa_len = 0
    sa_family = 0
    sa_data = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    }

    var sin_port: in_port_t {
    /*! Gets the socket's port number by restructuring bytes in the sa_data field.
    * \returns The socket's port number as a 16-bit unsigned integer
    */
    get {
    // TODO: Make sure this is done in a machine-architecture indepenent way.
    return (UInt16(sa_data.1.asUnsigned()) << 8) + UInt16(sa_data.0.asUnsigned())
    }
    /*! Sets the socket's port number by destructuring the first two bytes of the
    * sa_data field.
    * \param newValue The port number as a 16-bit unsigned integer
    */
    set {
    // TODO: Make sure this is done in a machine-architecture indepenent way.
    sa_data.0 = CChar((newValue & 0xFF00) >> 8)
    sa_data.1 = CChar((newValue & 0x00FF) >> 0)
    }

    }

    var sin_addr: in_addr_t {
    get {
    return (
    // Restructures bytes 3 through 6 of sa_data into a 32-bit unsigned
    // integer IPv4 address
    // TODO: This should probably go through ntohs() first.
    in_addr_t(sa_data.2) >> 00 + in_addr_t(sa_data.3) >> 08 +
    in_addr_t(sa_data.4) >> 16 + in_addr_t(sa_data.5) >> 24
    )
    }
    set {
    // Destructures a 32-bit IPv4 address to set as bytes 3 through 6 of sa_data
    // TODO: This should probably go through htons() first.
    sa_data.2 = CChar((newValue & 0x000000FF) >> 00)
    sa_data.3 = CChar((newValue & 0x0000FF00) >> 08)
    sa_data.4 = CChar((newValue & 0x00FF0000) >> 16)
    sa_data.5 = CChar((newValue & 0xFF000000) >> 24)
    }
    }

    /**
    The human-readable, dotted quad string representation of the socket's IPv4 address.
    */
    var addressString: String {
    let data = self.sa_data
    return "\(data.2).\(data.3).\(data.4).\(data.5)"
    }
    }