aboutsummaryrefslogtreecommitdiff
path: root/src/unix/UnixSocket.cpp.hx
blob: 32a57d224a64889deb1978e648eda31cb33ce326 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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);
    }
}