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