Project

General

Profile

How to write server code for wstunnel server and establish communication using unix sockets?

Added by N_Aditya over 4 years ago

Hello,

I'm using lighttpd 1.4.54 on ubuntu 18.04.

This is the server code (b.py):

#!/usr/bin/python3
import socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind('/home/ubuntu/a.sock')
sock.listen(1)
conn, addr = sock.accept()
while 1:
    conn.sendall(bytes("hi", 'UTF-8'))
    sleep(1)

Gave execution rights to everyone "chmod +x b.py".

I've configured websocket tunnel like this:

wstunnel.server = ( 
    "/websocket" => ( (
        "socket" => "/home/ubuntu/a.sock",
        "bin-path" => "/home/ubuntu/b.py",
        "max-procs" => 1,
    ))
)

According to what I've read, lighttpd should spawn b.py (code given above) and it should also redirect the stdout, stdin of this process (b.py) to socket a.sock and thus lighttpd and the process will communicate using this socket.
When lighttpd starts, it spawns b.py but messages sent to lighttpd are not redirected to b.py and the output of b.py is not redicted to lighttpd.
The problem is client (javascript code on Google Chrome browser) doesn't receive any data.

Thanks.


Replies (6)

RE: How to start a fastcgi using mod_wstunnel and establish communication using unix sockets? - Added by stbuehler over 4 years ago

wstunnel doesn't use FastCGI to talk to the backend, it uses a plain "stream" (TCP/unix) socket (as described at the top of Docs_ModWStunnel). You also didn't say how you are trying to talk to lighttpd, as you'll need a websocket client in this case.

RE: How to start a fastcgi using mod_wstunnel and establish communication using unix sockets? - Added by N_Aditya over 4 years ago

If it uses plain stream data, then normal writing to stdout and reading from stdin should work, right?
I tried that too, but reading from stdin returns '\0' only.
I wrote JavaScript code as client. When client sends data, it is successfully received by lighttpd (confirmed using wireshark).

RE: How to start a fastcgi using mod_wstunnel and establish communication using unix sockets? - Added by stbuehler over 4 years ago

N_Aditya wrote:

If it uses plain stream data, then normal writing to stdout and reading from stdin should work, right?
I tried that too, but reading from stdin returns '\0' only.

No. mod_wstunnel will create a new connection to the ("listening") socket of the backend; the backend needs to accept the connection (resulting in a new socket) and communicate through that. Afaik the listening socket will be passed on fd 0 ("stdin") to the backend (same as with FastCGI).

FCGI_Accept() only handles a single connection at a time, and (iirc) redirects stdin/stdout to that socket (I think it involved very ugly magic). But the connection doesn't use the FastCGI protocol, so you can't use FCGI_Accept().

I wrote JavaScript code as client. When client sends data, it is successfully received by lighttpd (confirmed using wireshark).

I recommend running "strace -p $(pidof a.out)" to see what your backend is doing.

RE: How to start a fastcgi using mod_wstunnel and establish communication using unix sockets? - Added by N_Aditya over 4 years ago

I have updated the question, please check the new code.

No. mod_wstunnel will create a new connection to the ("listening") socket of the backend; the backend needs to accept the connection (resulting in a new socket) and communicate through that. Afaik the listening socket will be passed on fd 0 ("stdin") to the backend (same as with FastCGI).

I changed the code and now it creates the a.sock socket and calls listen(). I did not receive any data here and lighttpd didn't connect to it. I used "strace -p (pid of lighttpd)" to verify this. No data was read by lighttpd.

Also, lighttpd creates a.sock-0 socket at the same location and listens. I successfully connected to it using server side code and sent data to it using client, but server code did not receive any data on this socket. Output of strace:

read(7, "\201\210\345_!:\324m\22\16\322g\30\32", 4095) = 14
writev(8, [{iov_base="1234789 ", iov_len=8}], 1) = 8

Lighttpd is writing to file descriptor 8.

Output of lsof on lighttpd:

COMMAND    PID     USER   FD      TYPE             DEVICE SIZE/OFF   NODE NAME
lighttpd 20584 lighttpd    0u      CHR                1,3      0t0      6 /dev/null
lighttpd 20584 lighttpd    1u      CHR                1,3      0t0      6 /dev/null
lighttpd 20584 lighttpd    2u      CHR                1,3      0t0      6 /dev/null
lighttpd 20584 lighttpd    3w      REG               0,22        6    548 /run/lighttpd.pid
lighttpd 20584 lighttpd    4u     IPv4             163438      0t0    TCP *:http (LISTEN)
lighttpd 20584 lighttpd    5w      REG                8,1     4313 136809 /var/log/lighttpd/error.log
lighttpd 20584 lighttpd    6u  a_inode               0,13        0   9845 [eventpoll]
lighttpd 20584 lighttpd    7u     IPv4             165462      0t0    TCP localhost:http->localhost:39660 (ESTABLISHED)
lighttpd 20584 lighttpd    8u     unix 0xffff91d534622c00      0t0 165464 type=STREAM

lsof on server code:

COMMAND    PID     USER   FD      TYPE             DEVICE SIZE/OFF   NODE NAME
b.py    20585 lighttpd    0u     unix 0xffff91d647eae800      0t0  163445 /home/ubuntu/temp/a.sock-0 type=STREAM
b.py    20585 lighttpd    1u      CHR                1,3      0t0       6 /dev/null
b.py    20585 lighttpd    2u      CHR                1,3      0t0       6 /dev/null
b.py    20585 lighttpd    3r  a_inode               0,13        0    9845 inotify
b.py    20585 lighttpd    4u     unix 0xffff91d647eaf000      0t0  163465 /home/ubuntu/temp/a.sock type=STREAM
b.py    20585 lighttpd    5u     unix 0xffff91d647eaf400      0t0  163466 type=STREAM

Here b.py is new server code (replacing a.out) that creates a.sock and sends "hi" at intervals of 1 second.
I used "nc -U a.sock-0" to listen but it received nothing.

I recommend running "strace -p $(pidof a.out)" to see what your backend is doing.

Backend is writing data to a.sock. I verified that.

It would be great if you could provide a link to sample server code (and .conf file) that can be used with mod_wstunnel.

Thanks.

RE: How to write server code for wstunnel server and establish communication using unix sockets? - Added by stbuehler over 4 years ago

If you spawn the backend with lighttpd (i.e. configure "bin-path"), the backend needs to listen on the socket passed via fd 0. You can also spawn manually to run with different permissions or even on other hosts (that is basically what spawn-fcgi is for).

lighttpd will append a "proc number" to the socket if you pass "bin-path" (that is related to the respawn behaviour); that is why you see "a.sock-0" on fd 0. If you don't pass "bin-path" it will use the exact socket name as given, and you need to spawn the backend yourself.

"Simple" example handling one websocket connection at a time (this is obviously not how you should do it: websocket connections live for a long time, and you need to handle them concurrently). This backend needs to be spawned via lighttpd or spawn-fcgi (to pass a bound socket on fd 0).

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

int read_with_retry(int fd, char* buf, size_t len) {
    if (0 == len) return 0;
    for (;;) {
        int r = read(fd, buf, len);
        if (r < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                fprintf(stderr, "Failed reading data: %s\n", strerror(errno));
                return -1;
            }
        }
        return r;
    }
}

int writeall(int fd, char const* buf, size_t len) {
    while (len > 0) {
        int r = write(fd, buf, len);
        if (r < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                fprintf(stderr, "Failed writing data: %s\n", strerror(errno));
                return -1;
            }
        }
        len -= r;
        buf += r;
    }
    return 0;
}

int main() {
    int sock;

    while (-1 != (sock = accept(0, NULL, NULL))) {
        for (;;) {
            char buf[1024];
            int r = read_with_retry(sock, buf, sizeof(buf));
            if (r < 0) {
                close(sock);
                break;
            }
            if (r == 0) {
                fprintf(stderr, "Connection closed by webserver\n");
                close(sock);
                break;
            }
            if (writeall(sock, buf, r) < 0) {
                close(sock);
                break;
            }
        }
    }
}

RE: How to write server code for wstunnel server and establish communication using unix sockets? - Added by N_Aditya over 4 years ago

stbuehler wrote:

If you spawn the backend with lighttpd (i.e. configure "bin-path"), the backend needs to listen on the socket passed via fd 0. You can also spawn manually to run with different permissions or even on other hosts (that is basically what spawn-fcgi is for).

Thank you for the code and quick response!!

    (1-6/6)