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; 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); } }