Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I set the file descriptors for a new Process in Haxe to use it with a socket?

Tags:

python

haxe

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

Edit 1

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'.

like image 319
Lord Ratte Avatar asked Nov 07 '22 01:11

Lord Ratte


1 Answers

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.

like image 111
YellowAfterlife Avatar answered Nov 14 '22 22:11

YellowAfterlife