curl / Mailing Lists / curl-library / Single Mail
Buy commercial curl support from WolfSSL. We help you work out your issues, debug your libcurl applications, use the API, port to new platforms, add new features and more. With a team lead by the curl founder himself.

Experimental websocket API

From: Thomas Glanzmann via curl-library <curl-library_at_lists.haxx.se>
Date: Sat, 29 Oct 2022 09:12:20 +0200

Hello,
after reading about the experimental websocket in the release notes, I
had to try it. I use curl to tunnel RDP (Windows Remote Desktop) over
websockets. So I tried to adopt my existing code to use the new API. I
noticed a few things:

        - curl_ws_send returns the number payload bytes sent + websocket
          header. At least from my point of view, I'm not really
          interested in the websocket header bytes written but only in the
          number of payload bytes written so that I can handle 'short
          writes'. Anyway I can just subtract '6' (apparently the size
          of an outoing websocket header) from the number of bytes
          returned and go with that.

        - As server I used a small go websocket server that works with
          websocat[1]. When I send data using libcurl/websocket the go
          websocket server sends me a FIN BIT 1 but I requested for the
          connection to stay open.

        - If the remote websocket connection terminates by either sending a WS
          FIN Bit or I just kill it with ctrl-c, curl 'keeps the connection
          open' and lets me read using curl_ws_recv without reporting an
          error. When I press Ctrl-D in belows example applicagtion, it
          opens a new connection.

In order to have some code to show, I wrote a little bit of code which
demonstrates the issues I'm having. I'm probably doing also something
stupid and using the API wrong, but here it goes anyway:

        - 'firepass' go server (can be build with 'make' as long as go
          is installed) listens on localhost Port 12345. If someone
          connects to http://127.0.0.1:12345/rdp with a websocket it
          bridges that websocket connection to 127.0.0.1 Port 22 aka
          local ssh server on the default port.

        - wsconnect (can be build on Linux with 'make' as long as
          curl-config of a websocket enabled curl is first in the path)
          connects stdin/stdoud to http://127.0.0.1:12345/rdp.

Things I noticed:

        - With websocat I can conenct to the go server:

(mini) [~/websocat] ssh -o ProxyCommand="target/release/websocat -b ws://127.0.0.1:12345/rdp" localhost
Warning: Permanently added 'localhost' (ED25519) to the list of known hosts.
Linux mini 6.1.0-rc2-asahi-ARCH+ #6 SMP PREEMPT_DYNAMIC Thu Oct 27 18:34:27 CEST 2022 aarch64
Last login: Sat Oct 29 08:52:47 2022 from 127.0.0.1
(mini) [~] who am i
sithglan pts/8 Oct 29 08:53 (127.0.0.1)
(mini) [~] exit
logout
Connection to localhost closed.

        - With libcurl, I get the Openssh prompt, but also receive a FIN
          BIT 1 immediately upon connect. But I also noticed if the
          remote does not send anything (like with RDP) I get a fin bit
          from the remote on the reply of the first message. Libcurl
          and/or my application does not pick up that the connection is
          closed and continues to use it:

(mini) [~/curlwebsocketplayground] ./wsconnect
* Trying 127.0.0.1:12345...
* Connected to 127.0.0.1 (127.0.0.1) port 12345 (#0)
> GET /rdp HTTP/1.1
Host: 127.0.0.1:12345
Accept: */*
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: Gq02qNqZUWv0Dl8PQ+6/qQ==

* Mark bundle as not supporting multiuse
< HTTP/1.1 101 Switching Protocols
< Upgrade: websocket
< Connection: Upgrade
< Sec-WebSocket-Accept: SYTpjxLbBy6ptoB4ryB3jdHYEg8=
* Received 101, switch to WebSocket; mask 7ea6ec62
<
* Connection #0 to host 127.0.0.1 left intact
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
remotefd: trying to read
* WS: got 37 websocket bytes to decode
* WS:237 received FIN bit 1
* WS: received OPCODE BINARY
* WS: received 35 bytes payload (0 left)
remotefd: 35 bytes read
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
STDOUT_FILENO: ready to send
STDOUT_FILENO: trying to send 35 bytes
SSH-2.0-OpenSSH_9.0p1 Debian-1+b2
STDOUT_FILENO: 35 bytes (ALL) sent. from empty.
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
test
STDIN_FILENO: trying to read
STDIN_FILENO: 5 bytes read
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
remotefd: ready to send
remotefd: trying to send 5 bytes
* WS: send OPCODE BINARY
* WS: send FIN bit 0 (byte 00)
* WS: send payload len 5
* WS: wanted to send 11 bytes, sent 11 bytes
remotefd: 5 bytes (ALL) sent. to empty.
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
remotefd: trying to read
* WS: got 26 websocket bytes to decode
* WS:237 received FIN bit 1
* WS: received OPCODE CLOSE
* WS: received 24 bytes payload (0 left)
remotefd: 24 bytes read
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
STDOUT_FILENO: ready to send
STDOUT_FILENO: trying to send 24 bytes
continuation after FINSTDOUT_FILENO: 24 bytes (ALL) sent. from empty.
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
websocket: continuation after FIN

        - Using the libcurl/wsconnect I'm not able to get a ssh connection:

(mini) [~/curlwebsocketplayground] ssh -o ProxyCommand="./wsconnect" localhost
* Trying 127.0.0.1:12345...
* Connected to 127.0.0.1 (127.0.0.1) port 12345 (#0)
> GET /rdp HTTP/1.1
Host: 127.0.0.1:12345
Accept: */*
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: Nm/meDaQgcBWdyLfb5tRfw==

* Mark bundle as not supporting multiuse
< HTTP/1.1 101 Switching Protocols
< Upgrade: websocket
< Connection: Upgrade
< Sec-WebSocket-Accept: pNv2JrXQJQqgJ97d/HVxJ3Eo6j8=
* Received 101, switch to WebSocket; mask c713068f
<
* Connection #0 to host 127.0.0.1 left intact
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
STDIN_FILENO: trying to read
STDIN_FILENO: 35 bytes read
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
remotefd: ready to send
remotefd: trying to send 35 bytes
* WS: send OPCODE BINARY
* WS: send FIN bit 0 (byte 00)
* WS: send payload len 35
* WS: wanted to send 41 bytes, sent 41 bytes
remotefd: 35 bytes (ALL) sent. to empty.
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
websocket: continuation after FIN
remotefd: trying to read
* WS: got 26 websocket bytes to decode
* WS:237 received FIN bit 1
* WS: received OPCODE CLOSE
* WS: received 24 bytes payload (0 left)
remotefd: 24 bytes read
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
STDOUT_FILENO: ready to send
STDOUT_FILENO: trying to send 24 bytes
STDOUT_FILENO: 24 bytes (ALL) sent. from empty.
FD_ZERO
STDIN_FILENO: ready to receive
remotefd: ready to receive
websocket: close sent

My example code is here:

https://tg.st/u/curlwebsocketplayground.tar.gz

I would like to know if I'm using it wrong or there are still some rough
edges that need to be worked on.

It would also be nice to be able to use curl as a websocat replacement
like that, I tried but it does not work at the moment.

ssh -o ProxyCommand="curl ws://127.0.0.1:12345/rdp" localhost

[1] https://github.com/vi/websocat

Cheers,
        Thomas
-- 
Unsubscribe: https://lists.haxx.se/listinfo/curl-library
Etiquette:   https://curl.se/mail/etiquette.html
Received on 2022-10-29