Linux
rust

pipeをweb socketにつなぐコマンドをRustで書いてみた

wspipe

Linuxのプロセス間通信であるpipeをweb socketにつないで、インターネットをまたいで使えるようなコマンドをRustで書いてみました。

ソースコードはgithub に上げました。
https://github.com/tetsu-koba/wspipe-rs

使用方法

stdin2wsws2stdout という2つのコマンドがあります。
ws2stdout が受信側でありweb socketを開いてlistenします。
そこにstdin2wsで送信することができます。

例として、受信側(ipアドレスは192.168.10.120)では以下のようにしてポート8001にweb socketを作って待ち受けます。

./ws2stdout 0.0.0.0:8001 |tee kern.log

それに対して、以下のように/var/log/kern.log をwebsocket経由で流すことができます。

sudo tail -f /var/log/kern.log | ./stdin2ws ws://192.168.10.120:8001

これはある実験のために作ったのですが、それに関しては次の記事で。
(2018.10.10 書きました。Websocketで簡単に音声のストリーミングをしてみる)

つまずいたところ

websocketにはws-rs のクレートを使用しています。
https://crates.io/crates/ws

pub fn stdin2websocket(url: &str) {
    let (sender, receiver) = channel::<u32>();
    if let Err(error) = connect(url, |out| {
        let s2 = sender.clone();
        thread::spawn(move || {
            send_binary(&out);
            thread::sleep(time::Duration::from_millis(100));
            out.close(CloseCode::Normal).unwrap();
            s2.send(1).unwrap();
        });

        msg_handler
    })
    {
        println!("Failed to create WebSocket due to: {:?}", error);
    }
    receiver.recv().unwrap();
}

ws::connect() の引数で渡すクロージャの中でスレッドを起こしてstdinからwebsocketに転送をするのですが、このスレッドの終了を待つ方法で苦労しました。
このクロージャが FnMut であり、複数回実行される可能性のあるものであるため、単純に所有権をmove させることができないのです。コンパイルエラーになります。
channelのsenderをcloneして渡せばよいということに気がつくまでかなりかかりました。
channelはreceiverはcloneできませんが、senderはclone可能です。
(パッケージ名のmpscはMulti-producer, single-consumer の意味)

また、転送終了後にcloseする前に100msのwaitを入れましたが、これが無いとcloseがエラーになります。まだwebsocketの処理が完了していないためと思われるのですが、ちょっと解せません。バグを踏んでいるのか、やり方が間違っているのか。