Socket Listener in Ruby
I was in a situation where I wanted to test out the port forwarding configuration of a client to a server. This is to configure a resource so a long process’ callback can happen locally and I want to ensure the port forwarding was happening correctly with different tools (otherwise, there’s lots of waiting.)
A simple way to test is to listen to the port and just make a similar
call using curl
, right?
The curl
part is trivial, create a request, just like to any server.
The listener part… well, I thought I could get a ready made application that does this, which prints anything received on the port and have it printed to the screen.
Simple right?
Well, my search for a listener program only resulted in netstat, which tells me all the connected programs to each port (super useful if you want to kill programs based on ports - cough microservice architecture cough)
Wireshark was one I thought that could ‘sniff out’ what’s coming in over a port, but it is overwhelming as Wireshark reports EVERYTHING going through the network. Too much for my use case.
At the end, I found a quick article that wrote a listener program in Ruby and modified it further to write request contents to the screen and send a proper HTTP response back.
The original program from Tutorials Point
1
2
3
4
5
6
7
8
9
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Servers run forever
client = server.accept # Wait for a client to connect
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Disconnect from the client
}
My final version:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require 'socket'
port_to_listen_to = 8080
puts "starting to listen to: #{port_to_listen_to}"
server = TCPServer.open(port_to_listen_to)
loop {
client = server.accept
puts 'receiving data ' + Time.now.ctime
puts '--------------------------------------------------------------------------------'
while (a = client.gets) != "\r\n" do
puts a
end
puts '--------------------------------------------------------------------------------'
client.puts "HTTP/1.1 200 Success"
client.puts ""
client.puts "Success\n"
client.close
}
My changes
I added changes to the original version, specifically these areas:
\r\n
marks the end of the HTTP header in the HTTP 1.1 spec, so print everything until encountering this value.puts
prints section breaks and incoming data to the screen.client.puts
sends a HTTP 1.1 formatted message so other web clients such as Postman responds properly.
Testing out
Let’s see results using this new Ruby script by making a request, seeing the listener output, and the client’s output.
To set up, install Ruby, write the above contents into a file named:
listener.rb
and run the listener with command: $ ruby listener.rb
With modern security or firewall set up, testing is easiest on the same computer.
This test requires a minimum of one computer with two terminals: one
to run the listener program and another to make the curl
request.
Request
I will make a request using curl
, it’s sufficient for this use case.
Listener Result
With the listener program running, this is the output:
Client Result
The client sees:
Tada~!
Other tips
Listening to ports less than 1024 with the script was tricky because those ports require root access.
The way I set this up: changed to root and installed rvm, to install ruby.
If the goal is to listen to ports <1024, definitely run as root, or find a way to run a ruby program as root.
Possible Future Work
Getting this to work on a basic level was enough, but for the future there are areas to investigate or improve:
- Use Traveling Ruby to
make a portable version so running as root would be as easy as:
$ sudo listener
? - Print out additional information from the request, especially a
POST
request’s body. Hint: probably use thecontent-length
field.
Conclusion
This was fun delving into networking guts and making a useful utility
that I expected to be present for every system already (maybe I just
need to read more man
pages?)