diff options
-rw-r--r-- | CHANGELOG | 3 | ||||
-rw-r--r-- | LICENSE | 21 | ||||
-rw-r--r-- | README | 20 | ||||
-rw-r--r-- | build-examples.hxml | 10 | ||||
-rw-r--r-- | examples/Client.hx | 19 | ||||
-rw-r--r-- | examples/Server.hx | 29 | ||||
-rwxr-xr-x | generate-docs.sh | 17 | ||||
-rw-r--r-- | haxelib.json | 14 | ||||
-rw-r--r-- | src/externs/Build.xml | 9 | ||||
-rw-r--r-- | src/externs/Socket.cpp.hx | 32 | ||||
-rw-r--r-- | src/unix/UnixSocket.cpp.hx | 150 | ||||
-rw-r--r-- | src/unix/UnixSocket.hx | 8 |
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. @@ -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. @@ -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 {} |