39

I have created a Node.JS application that provides a web socket server (using npm ws). This websocket waits until a connection is established. Once a connection hits the server, the websocket executes a job. When the job is done, a message is sent over the socket, then the socket is being closed. This socket works as expected; already tested it with another Node.JS script.

How can I connect to the web socket using only linux command line tools? I already tried curl as described here. However, I can not find out how to connect properly to my websocket which runs at localhost:8088/socket/

Edit: My question has been identified as a possible duplicate of another question. However, the linked question only asks if there is a way to do it with curl. I'd be glad to see any solution which works on bash. Also, the answer to the linked question is a javascript file using autobahn.ws

0

6 Answers 6

51

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Hi! Thanks for the tool. Is it possible to install it permissionless ? At the office we do not have sudo access
@GeorgiosPligoropoulos Yes, just download appropriate pre-built executable from Github releases and run it. Multiple variants may be runnable. You can also download and extract deb package.
When I try: websocat -H "Authorization: Bearer ...." ws://server/url, I get the help message. It seems that this tool doesn't handle listening on a web socket with options.
@NicolasRouquette In this mode options should be specified after the URL to connect. Note that headers for listening and for connecting are separate options.
29

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.org HTTP/1.1 101 Web Socket Protocol Handshake Upgrade: WebSocket Connection: Upgrade WebSocket-Origin: http://www.websocket.org WebSocket-Location: ws://echo.websocket.org/ Server: Kaazing Gateway Date: Mon, 11 Jun 2012 16:34:46 GMT Access-Control-Allow-Origin: http://www.websocket.org Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: content-type Access-Control-Allow-Headers: authorization Access-Control-Allow-Headers: x-websocket-extensions Access-Control-Allow-Headers: x-websocket-version Access-Control-Allow-Headers: x-websocket-protocol 

9 Comments

... which I have already mentioned in my question :P No seriousely: I can't get it to work with localhost. I get a HTTP/1.1 400 Bad Request Content-type: text/html on my express.js webserver
Oh I'm sorry! Maybe you need to handle the update request yourself then? if the server only serves the ws protocol? Because if I understand right, the curl request tries to do a http and update it to a ws protocol - but if the webserver doesn't accept the http incoming calls, nothing can happen stackoverflow.com/questions/18045352/…
Ok so I thought about what you said. I have'nt heard of this upgrade mechanism before. However, this could of course be the reason why it doesn't work ;) So I have uploaded my node.js app to a online cloud provider now. Therefore I have a http url now. I guess that my express.js framework could internally use the upgrade mechansim, too. How can I add my path and port to the curl command?
Okay. I was curious what will happen if the connection establishes after the upgrade. Because it is a bidirectional stream, it must be open in the terminal somehow. But how? What will happen? Then I tried this and got this: curl: (1) Protocol "ws" not supported or disabled in libcurl From this one: curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: echo.websocket.org" -H "Origin:http://www.websocket.org" ws://echo.websocket.org/
Okay, so to answer your question: I think you can not go that far and do the communication after the upgrade with the linux curl tool. Since you asked about linux tools, maybe it could be possible to install something like this github.com/progrium/wssh and run then from command line (which can handle the protocol)
|
23

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.org wsport=80 exec 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\r Host: ${wshost}\r Connection: Upgrade\r Upgrade: websocket\r Sec-WebSocket-Key: $(echo -n "somekey"|base64)\r Origin: 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 | base64 eXT5yQBZ/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.

4 Comments

Love this dive into the essentials of tcp and websockets, thanks!
Love this dive too, thanks ! Did you go forward in your masking function ? I'm also in interrested in a 'bash only' solution...
Sorry. I've delayed that for too long, and now that websocket.org is redirecting to https, so, my samples do not work anymore. I can use openssl s_client, but it breaks the "bash only" premise. I still want to implement that. But that is not an easy task, and currently, I am using my free time to develop my own programming language. Later, I will implement WebSockets there, and then I will create a fully working bash script to test it, and then I will update this post.
If you are comfortable with doing things in complete violation of the standard (you are on a trusted local connection), then you can do this to "mask" the data: (printf "%04x%04x%08x\n" "33278" "${#DATA}" "0" | xxd -p -r; printf "${DATA}\n") >&3 Note that as this is currently written it only works for text, and only for packets less than 2^16 bytes (though you can modify this example to work in other situations).
14

I've been using wscat, an npm package. https://github.com/websockets/wscat.

Instructions from the package readme at the time of posting:

Installation

This module needs to be installed globally so use the -g flag when installing:

npm install -g wscat 

Example

$ wscat -c ws://echo.websocket.org Connected (press CTRL+C to quit) > hi there < hi there > are you a happy parrot? < are you a happy parrot? 

1 Comment

Note that ws://echo.websocket.org service is no longer available
1

tl;dr curl -H 'Upgrade: websocket' -H "Sec-WebSocket-Key: `openssl rand -base64 16`" -H 'Sec-WebSocket-Version: 13' --http1.1 -sSv https://ws.ifelse.io (depending on the server you might need to provide Origin and/or Connection: Upgrade)

First, how does the websocket protocol work in a few words? A client connects to a server, sends a handshake request, receives "101 Switching Protocols" (a handshake response) after which they send frames back and forth.

The handshake looks along the following lines:

GET / HTTP/1.1 Host: ws.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Version: 13 Origin: http://example.com HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= 

Upgrade makes it switch from HTTP(s) to the websocket protocol.

Connection specifies that Upgrade is a hop-by-hop header (headers that intermediaries should consume, not forward). But my experiments show that it works w/o this header. Or to be more precise, it might be optional if there's a reverse proxy in front of the server (e.g. nginx).

Sec-WebSocket-Key/Sec-WebSocket-Accept is a security measure described here, here and here.

Sec-WebSocket-Version specifies the websocket protocol version. According to RFC 6455 it should be equal 13.

Origin is needed when the client is a browser and the origin of the requesting page doesn't match the origin of the websocket server URL.

A detailed description of what should constitute a websocket handshake request can be found here.

That's how it goes with HTTP/1.1. HTTP/2 is a different story.

Knowing this to establish a websocket connection with curl:

$ curl -H 'Upgrade: websocket' \ -H "Sec-WebSocket-Key: `openssl rand -base64 16`" \ -H 'Sec-WebSocket-Version: 13' \ --http1.1 \ -sSv \ https://ws.ifelse.io ... > GET / HTTP/1.1 > Host: ws.ifelse.io > Upgrade: websocket > Sec-WebSocket-Key: e2dujvcbYbN747lapeH+WA== > Sec-WebSocket-Version: 13 ... < HTTP/1.1 101 Switching Protocols < Connection: upgrade < upgrade: websocket < sec-websocket-accept: 6wmMGMtN00aWw3loYd6P36EHKMI= 

The other options are wscat, websocat.

Comments

1

My project wget is developed to solve this problem. It provides an interactive command-line interface that allows you to connect to a WebSocket server, send messages, and receive responses.

to install latest wsget version you can use following command:

go install github.com/ksysoev/wsget/cmd/wsget@latest 

To use wsget, simply run the following command:

wsget "wss://ws.postman-echo.com/raw" Use Enter to input request and send it, Ctrl+C to exit -> { "ping": 1 } <- { "ping": 1 } 

This tool makes it easy to interact with WebSocket servers directly from the command line.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.