I am translating some code to Haxe from Python so that I can target more platforms. But I'm having trouble with the following snippet.
import socket
from subprocess import Popen
host='127.0.0.1'
port=8080
file='handle.sh'
handler = socket.socket()
handler.bind((host, port))
handler.listen(5)
conn, address = handler.accept() # Wait for something to connect to the socket
proc = Popen(['bash', file], stdout=conn.makefile('wb'), stdin=conn.makefile('rb'))
proc.wait()
conn.shutdown(socket.SHUT_RDWR)
conn.close()
In Python, I can set stdin and stdout to the relevant file descriptors of the socket. But by the time I call shutdown, all the data to be sent is in the right buffer and nothing blocks me.
But I can't do this in Haxe as far as I can tell because input and output from the socket and, stdin and stdout from the process are all read-only.
I seem to get a deadlock with whatever I try. Currently I'm trying with a thread but it still gets stuck at reading from the socket.
#!/usr/bin/haxe --interp
import sys.net.Host;
import sys.net.Socket;
import sys.io.Process;
import sys.thread.Thread;
class HaxeServer {
static function main() {
var socket = new Socket();
var fname = 'handle.sh';
var host = '127.0.0.1';
var port = 8080;
socket.bind(new Host(host), port);
socket.listen(5);
while (true) {
var conn = socket.accept();
var proc = new Process('bash', [fname]);
exchange(conn, proc);
conn.output.write(proc.stdout.readAll());
proc.close();
conn.shutdown(true, true);
conn.close();
}
}
static function exchange(conn:Socket, proc:Process):Void {
#if (target.threaded)
Thread.create(() -> {
while (true) {
var drip = conn.input.readByte();
proc.stdin.writeByte(drip);
}
});
#end
}
}
Attempting to use the answer posted by @YellowAfterlife, I ran the following code instead of my exchange function.
conn.setBlocking(false);
Thread.create( () -> {
trace('--> read');
while (true) {
trace('-1a');
var data:Bytes = readAllNonBlocking(conn.input).bytes;
trace('-2a');
proc.stdin.write(data);
trace('-3a');
proc.stdin.flush();
}
});
trace('--> write');
while (true) {
trace('-1b');
var data:Bytes = readAllNonBlocking(proc.stdout).bytes;
trace('-2b');
conn.output.write(data);
trace('-3b');
conn.output.flush();
}
trace('Wait');
trace(proc.exitCode());
but it just logs this and hangs:
HaxeServer.hx:42: --> write
HaxeServer.hx:44: -1b
So it's not even getting into the thread and the input is still blocking. If I put both read-write sections in threads, it just prints 'Wait'.
I have previously dealt (on a project bridging unrelated network APIs - GitHub repo) with the issue of reading all available data without deadlocking by marking the socket as non-blocking and implementing a custom function that reads all available data, like so:
public static function readAllNonBlocking(input:Input):{bytes:Bytes,eof:Bool} {
var total:BytesBuffer = new BytesBuffer();
var eof = false;
var len = 0;
try {
while (true) {
total.addByte(input.readByte());
len += 1;
}
} catch (x:Error) {
switch (x) {
case Blocked: // OK!
default: throw x;
}
} catch (x:Eof) {
eof = true;
}
var bytes:Bytes = total.getBytes();
if (bytes.length > len) {
bytes = bytes.sub(0, len);
}
return { bytes: bytes, eof: eof };
}
You will also likely need to .flush()
stdin for data to actually make it to the process.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With