Linux Bash: How to open a websocket connection as client Linux Bash: How to open a websocket connection as client curl curl

Linux Bash: How to open a websocket connection as client


My tool websocat is specifically designed for this.

websocat ws://your_server/url

You can connect and exchange data with your server. By default each line becomes a WebSocket text message and vice versa.

On Linux it is more comfortable to play with it using readline:

rlwrap websocat ws://your_server/url.

It is not the only CLI websocket client. There are also "ws" and "wscat" projects.


Try this one from here: How to hit the WebSocket Endpoint?

$ curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: echo.websocket.org" -H "Origin: http://www.websocket.org" http://echo.websocket.org

Which he has from here: http://www.thenerdary.net/post/24889968081/debugging-websockets-with-curl

To quote the content from this site for the future:

Those flags say:

  1. Return headers in the output
  2. Don’t buffer the response
  3. Set a header that this connection needs to upgrade from HTTP to something else
  4. Set a header that this connection needs to upgrade to a WebSocket connection
  5. Set a header to define the host (required by later WebSocket standards)
  6. Set a header to define the origin of the request (required by later WebSocket standards)

If the websocket is working it should return the following:

$ curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: echo.websocket.org" -H "Origin:http://www.websocket.org" http://echo.websocket.orgHTTP/1.1 101 Web Socket Protocol HandshakeUpgrade: WebSocketConnection: UpgradeWebSocket-Origin: http://www.websocket.orgWebSocket-Location: ws://echo.websocket.org/Server: Kaazing GatewayDate: Mon, 11 Jun 2012 16:34:46 GMTAccess-Control-Allow-Origin: http://www.websocket.orgAccess-Control-Allow-Credentials: trueAccess-Control-Allow-Headers: content-typeAccess-Control-Allow-Headers: authorizationAccess-Control-Allow-Headers: x-websocket-extensionsAccess-Control-Allow-Headers: x-websocket-versionAccess-Control-Allow-Headers: x-websocket-protocol


The vi answer provides a very useful tool. websocat is so easy to use.

I want to do it only with bash builtins. I think it can be useful to others to understand better the protocol.

The websocket protocol handshake is easy.

To open a tcp channel in bash we can use:

wshost=echo.websocket.orgwsport=80exec 3<>/dev/tcp/${wshost}/${wsport}

Then we can read and write data to that tcp connection using file descriptor &3.

First let's open a read channel:

CR=$(echo -en "\r")while read <&3; do echo "WS MSG:[${REPLY//$CR/}]"; done &

We don't really need replace the carriage return bytes \r, but the terminal output is weird if we don't.

Each websocket server implementation can require specific details. websocket.org requires Origin header and \r byte before end line.

Then we can start the connection upgrade:

echo -e "GET / HTTP/1.1\rHost: ${wshost}\rConnection: Upgrade\rUpgrade: websocket\rSec-WebSocket-Accept: $(echo -n "somekey"|base64)\rOrigin: http://www.websocket.org\r\r" >&3

Imediatelly we see the ws upgrade output:

$ WS MSG:[HTTP/1.1 101 Web Socket Protocol Handshake]WS MSG:[Access-Control-Allow-Credentials: true]WS MSG:[Access-Control-Allow-Headers: content-type]WS MSG:[Access-Control-Allow-Headers: authorization]WS MSG:[Access-Control-Allow-Headers: x-websocket-extensions]WS MSG:[Access-Control-Allow-Headers: x-websocket-version]WS MSG:[Access-Control-Allow-Headers: x-websocket-protocol]WS MSG:[Access-Control-Allow-Origin: http://www.websocket.org]WS MSG:[Connection: Upgrade]WS MSG:[Date: Thu, 29 Oct 2020 15:08:01 GMT]WS MSG:[Sec-WebSocket-Accept: eXT5yQBZ/TOhFBUi6nLY8cfzs1s=]WS MSG:[Server: Kaazing Gateway]WS MSG:[Upgrade: websocket]WS MSG:[]

You see the Sec-WebSocket-Accept Header ? The server resolve it as described in RFC 6455. We can calculate it with:

$ echo -n "$(echo -n "somekey" | base64)258EAFA5-E914-47DA-95CA-C5AB0DC85B11" |    sha1sum | cut -d " " -f1 | xxd --ps -r  | base64eXT5yQBZ/TOhFBUi6nLY8cfzs1s=

I know... cut, xxd, base64 and sha1sum are not builtin, but this validation step is just for clarify.

The handshake are done and our tcp connection are now upgraded to a websocket connection.

Now the hard part.

We need to send data. We can learn how to do it in section 5 of the RFC6455.

a client MUST mask all frames that it sends to the server (see Section 5.3 for further details)

and

The server MUST close the connection upon receiving a frame that is not masked

Read the RFC Section 5.2 to know how to mask data.

The RFC even provides a ascii-art for data masking:

  0                   1                   2                   3  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len |    Extended payload length    | |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           | |N|V|V|V|       |S|             |   (if payload len==126/127)   | | |1|2|3|       |K|             |                               | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + |     Extended payload length continued, if payload len == 127  | + - - - - - - - - - - - - - - - +-------------------------------+ |                               |Masking-key, if MASK set to 1  | +-------------------------------+-------------------------------+ | Masking-key (continued)       |          Payload Data         | +-------------------------------- - - - - - - - - - - - - - - - + :                     Payload Data continued ...                : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |                     Payload Data continued ...                | +---------------------------------------------------------------+

I want to write a bash function to mask ws data, but I can not do it now. I'll update this post later.