aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--LICENSE21
-rw-r--r--README20
-rw-r--r--build-examples.hxml10
-rw-r--r--examples/Client.hx19
-rw-r--r--examples/Server.hx29
-rwxr-xr-xgenerate-docs.sh17
-rw-r--r--haxelib.json14
-rw-r--r--src/externs/Build.xml9
-rw-r--r--src/externs/Socket.cpp.hx32
-rw-r--r--src/unix/UnixSocket.cpp.hx150
-rw-r--r--src/unix/UnixSocket.hx8
12 files changed, 332 insertions, 0 deletions
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..5c61154
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,3 @@
+1.0.0
+-----
+Initial release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..870ac11
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright © 2024 rc_05
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README b/README
new file mode 100644
index 0000000..468643d
--- /dev/null
+++ b/README
@@ -0,0 +1,20 @@
+--- Haxe Unix Sockets ---
+
+Haxe bindings for Unix sockets.
+
+The following targets are supported:
+ - C++
+
+--- Installation ---
+
+$ haxelib git haxe-unix-sockets https://git.rc-05.com/haxe-unix-sockets
+
+--- Documentation ---
+
+The documentation is available at https://docs.rc-05.com/haxe-unix-sockets/git
+for the unstable version.
+
+--- License ---
+
+This project is licensed with the MIT License which can be viewed by
+reading the LICENSE file supplied with this codebase.
diff --git a/build-examples.hxml b/build-examples.hxml
new file mode 100644
index 0000000..4498ac9
--- /dev/null
+++ b/build-examples.hxml
@@ -0,0 +1,10 @@
+--cpp cpp
+--define analyzer-optimize
+--library haxe-unix-sockets
+--each
+
+--next
+--main examples.Server
+
+--next
+--main examples.Client
diff --git a/examples/Client.hx b/examples/Client.hx
new file mode 100644
index 0000000..675fe94
--- /dev/null
+++ b/examples/Client.hx
@@ -0,0 +1,19 @@
+package examples;
+
+import haxe.io.Bytes;
+import unix.UnixSocket;
+
+class Client {
+ static function main() {
+ var server = new UnixSocket("haxe.sock");
+ server.init();
+ server.connect();
+
+ server.writeBytes(Bytes.ofString("Ping"));
+
+ var receivedData = server.readBytes("Pong".length);
+ trace(receivedData);
+
+ server.close();
+ }
+}
diff --git a/examples/Server.hx b/examples/Server.hx
new file mode 100644
index 0000000..87e00a4
--- /dev/null
+++ b/examples/Server.hx
@@ -0,0 +1,29 @@
+package examples;
+
+import haxe.io.Bytes;
+import sys.FileSystem;
+import unix.UnixSocket;
+
+class Server {
+ static function main() {
+ var server = new UnixSocket("haxe.sock");
+
+ server.init();
+ server.bind();
+ server.listen(5);
+
+ while (true) {
+ trace('Waiting for clients to connect...');
+ var client = server.accept();
+ trace('Accepted connection from client with fd ${client.fileDescriptor}');
+ var receivedData = client.readBytes("Ping".length).toString();
+ trace('$receivedData');
+ client.writeBytes(Bytes.ofString("Pong"));
+ client.close();
+ }
+
+ if (FileSystem.exists("haxe.sock")) {
+ FileSystem.deleteFile("haxe.sock");
+ }
+ }
+}
diff --git a/generate-docs.sh b/generate-docs.sh
new file mode 100755
index 0000000..a633511
--- /dev/null
+++ b/generate-docs.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+SOURCE_PATH="https://git.rc-05.com/haxe-unix-sockets/tree/src"
+VERSION=${VERSION:-git}
+
+haxe --class-path src \
+ --no-output \
+ --each \
+ --next --cpp cpp --xml cpp/types.xml \
+ unix
+
+haxelib run dox \
+ -i cpp/types.xml \
+ -o docs \
+ --toplevel-package unix \
+ -D version $VERSION \
+ -D source-path $SOURCE_PATH
diff --git a/haxelib.json b/haxelib.json
new file mode 100644
index 0000000..82bb053
--- /dev/null
+++ b/haxelib.json
@@ -0,0 +1,14 @@
+{
+ "name": "haxe-unix-sockets",
+ "url" : "https://git.rc-05.com/haxe-unix-sockets",
+ "license": "MIT",
+ "tags": ["unix", "socket", "ipc"],
+ "description": "Haxe bindings to Unix sockets.",
+ "version": "1.0.0",
+ "classPath": "src",
+ "releasenote": "",
+ "contributors": [
+ "rc_05"
+ ],
+ "dependencies": {}
+}
diff --git a/src/externs/Build.xml b/src/externs/Build.xml
new file mode 100644
index 0000000..312314f
--- /dev/null
+++ b/src/externs/Build.xml
@@ -0,0 +1,9 @@
+<xml>
+ <section>
+ <files id="haxe" if="linux">
+ <compilerflag value="-I/usr/include"/>
+ <compilerflag value="-L/usr/lib"/>
+ <compilerflag value="-L/usr/lib64"/>
+ </files>
+ </section>
+</xml>
diff --git a/src/externs/Socket.cpp.hx b/src/externs/Socket.cpp.hx
new file mode 100644
index 0000000..6103cea
--- /dev/null
+++ b/src/externs/Socket.cpp.hx
@@ -0,0 +1,32 @@
+package externs;
+
+import cpp.Void;
+import cpp.SizeT;
+import cpp.Star;
+import cpp.Int32;
+import cpp.ConstStar;
+import cpp.ConstCharStar;
+
+@:include("sys/socket.h")
+@:native("struct sockaddr")
+@:structAccess
+extern class SockAddr {}
+
+@:include("sys/un.h")
+@:native("struct sockaddr_un")
+@:structAccess
+extern class SockAddrUnix {}
+
+@:buildXml("<include name='${haxelib:haxe-unix-sockets}/src/externs/Build.xml'/>")
+@:include("unistd.h")
+@:include("sys/socket.h")
+extern class Socket {
+ @:native("socket") static function socket(domain:Int32, type:Int32, protocol:Int32):Int32;
+ @:native("bind") static function bind(sockfd:Int32, addr:ConstStar<SockAddr>, addrlen:Int32):Int32;
+ @:native("listen") static function listen(sockfd:Int32, backlog:Int32):Int32;
+ @:native("accept") static function accept(sockfd:Int32, addr:Star<SockAddr>, addrlen:Star<Int32>):Int32;
+ @:native("connect") static function connect(sockfd:Int32, addr:ConstStar<SockAddr>, addrlen:Int32):Int32;
+ @:native("read") static function read(fd:Int32, buf:Star<Void>, count:SizeT):SizeT;
+ @:native("write") static function write(fd:Int32, buf:ConstStar<Void>, count:SizeT):SizeT;
+ @:native("close") static function close(fd:Int32):Int32;
+}
diff --git a/src/unix/UnixSocket.cpp.hx b/src/unix/UnixSocket.cpp.hx
new file mode 100644
index 0000000..32a57d2
--- /dev/null
+++ b/src/unix/UnixSocket.cpp.hx
@@ -0,0 +1,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);
+ }
+}
diff --git a/src/unix/UnixSocket.hx b/src/unix/UnixSocket.hx
new file mode 100644
index 0000000..1e5cea2
--- /dev/null
+++ b/src/unix/UnixSocket.hx
@@ -0,0 +1,8 @@
+package unix;
+
+/*
+ This is a dummy file.
+
+ See UnixSocket.<target>.hx for the actual implementation for each target.
+*/
+class UnixSocket {}