aboutsummaryrefslogblamecommitdiff
path: root/src/unix/UnixSocket.cpp.hx
blob: 32a57d224a64889deb1978e648eda31cb33ce326 (plain) (tree)





















































































































































                                                                                                                                       
package unix;

import cpp.RawPointer;
import cpp.Stdlib;
import cpp.UInt8;
import externs.Socket.SockAddr;
import externs.Socket.SockAddrUnix;
import externs.Socket;
import haxe.io.Bytes;

/**
    Unix socket for local interprocess communication.

    All the functions except for [readBytes](#readBytes) and [writeBytes](#writeBytes) must be called for the
    server socket.
**/
class UnixSocket {
    var nativeAddr:SockAddrUnix;
    var nativeBuffer:RawPointer<UInt8>;
    var nativeBufferLength = 0;

    /**
        Socket file path.
    **/
    public var path(default, null):String;

    /**
        File descriptor associated with the socket.
    **/
    public var fileDescriptor(default, null):Int;

    /**
        Configures the socket to work as a UNIX socket.
    **/
    function configure() {
        // Configure the socket to work as a UNIX one and set it up.
        nativeAddr = untyped __cpp__("{}");
        untyped __cpp__("nativeAddr.sun_family = AF_UNIX");
        for (i in 0...path.length) {
            untyped __cpp__("nativeAddr.sun_path[{0}] = {1}", i, path.charCodeAt(i));
        }
        #if debug
        trace('Configured socket with path $path and file descriptor $fileDescriptor');
        #end
    }

    /**
        Creates a new unconnected socket with a `path`.
    **/
    public function new(path:String) {
        // Trim the string so that it's always 107 characters long.
        this.path = path.substr(0, 108);
        nativeBufferLength = 1024;
        nativeBuffer = cast Stdlib.nativeMalloc(nativeBufferLength);

        configure();
    }

    /**
        Initializes the unconnected socket.
    **/
    public function init() {
        fileDescriptor = Socket.socket(untyped __cpp__("AF_UNIX"), untyped __cpp__("SOCK_STREAM"), 0);
    }

    /**
        Closes the socket.
    **/
    @:native("closeSocket")
    public function close() {
        Stdlib.nativeFree(cast nativeBuffer);
        Socket.close(fileDescriptor);
        #if debug
        trace('Succesfully closed socket with $path and file descriptor $fileDescriptor');
        #end
    }

    /**
        Binds the socket to the configured path.
    **/
    @:native("bindSocket")
    public function bind() {
        Socket.bind(fileDescriptor, untyped __cpp__("(const sockaddr *) &nativeAddr"), untyped __cpp__("sizeof(nativeAddr)"));
    }

    /**
        Instructs the socket to start listening to incoming connections while
        allowing for a maximum of `numMaxConnections` connections.
    **/
    @:native("listenSocket")
    public function listen(numMaxConnections:Int) {
        Socket.listen(fileDescriptor, numMaxConnections);
    }

    /**
        Accepts a client's connection and returns the client's socket.
    **/
    @:native("acceptSocket")
    public function accept():UnixSocket {
        var clientFileDescriptor = Socket.accept(fileDescriptor, untyped __cpp__("(sockaddr *) &nativeAddr"), untyped __cpp__("NULL"));
        var clientSocket = new UnixSocket(path);

        return clientSocket;
    }

    /**
        Connects to the socket.
    **/
    @:native("connectSocket")
    public function connect() {
        Socket.connect(fileDescriptor, untyped __cpp__("(const sockaddr *) &nativeAddr"), untyped __cpp__("sizeof(nativeAddr)"));
    }

    /**
        Reads `n` bytes from the socket.
    **/
    public function readBytes(n:Int):Bytes {
        var bytes = Bytes.alloc(n);

        if (n > nativeBufferLength) {
            nativeBufferLength *= 2;
            nativeBuffer = cast Stdlib.nativeRealloc(cast nativeBuffer, nativeBufferLength);
        }

        Socket.read(fileDescriptor, cast nativeBuffer, n);

        for (i in 0...n) {
            var byte = nativeBuffer[i];
            bytes.set(i, byte);
        }

        return bytes;
    }

    /**
        Writes `bytes` to the socket.
    **/
    public function writeBytes(bytes:Bytes) {
        if (bytes.length > nativeBufferLength) {
            nativeBufferLength *= 2;
            nativeBuffer = cast Stdlib.nativeRealloc(cast nativeBuffer, nativeBufferLength);
        }

        for (i in 0...bytes.length) {
            nativeBuffer[i] = bytes.get(i);
        }

        Socket.write(fileDescriptor, cast nativeBuffer, bytes.length);
    }
}