Cowsay Server - Part 2

This blog post was originally published on 2013/11/27

(This article is part 2 of 3 of my Cowsay Series of articles.)

This is the second post in a series of articles about writing my first application that uses sockets. For more information about why I'm doing this or how, please see my first article.

More Functional Requirements

I have a working server, but there are two things that bug me about it:

  1. I have to test it using netcat, which is good for simple stuff but things would be much easier with an actual client.

  2. Right now, the server just process a "raw" string of commands. I would rather have the server interpret parameters.

I figure that I'm going to need some type of "message format" to make requirement #2 work, so I first try to define that.

My Message Format

Since I'm familar with HTTP, I decided to use a message format that is very similar. Right now, I simply want to be able to pass a message and cow body format to the cowsay server. I therefore decided to send messages that look something like this:

BODY beavis.zen

That's it. Just plain old text (unicode?) over the wire with two properties. In the future, I'll probably want to use return codes and more header options.

The Client

Here's my first stab at a very simple client:

Github Gist

require 'socket'

module CowSay
    class Client
        class << self
            attr_accessor :host, :port

        # Convert our arguments into a document that we can send to the cowsay
        # Options:
        #   message: The message that you want the cow to say
        #   body: The cowsay body that you want to use
        def self.say(options)

            if !options[:message]
                raise "ERROR: Missing message argument"

            if !options[:body]
                options[:body] = "default"

            request <<EOF
MESSAGE #{options[:message]}
BODY    #{options[:body]}

        def self.request(string)
            # Create a new connection for each operation
            @client =, port)

            # Send EOF after writing the request

            # Read until EOF to get the response
end = 'localhost'
CowSay::Client.port = 4481

puts CowSay::Client.say message: 'this is cool!'
puts CowSay::Client.say message: 'This SUCKS!', body: 'beavis.zen'
puts CowSay::Client.say message: 'Moshi moshi!', body: 'hellokitty'

This is really a very simple socket client. I have one real method called say which understands two keys, message and body. I then take those values, drop them in a heredoc, and then send that to the server.

Of course, now that I'm using a new message format, I'm going to need to make some changes on the server too.

The Server, Part Two

Here's my stab at creating a server that can read the new message format:

Github Gist

require 'socket'

module CowSay
    class Server
        def initialize(port)
            # Create the underlying socket server
            @server =
            puts "Listening on port #{@server.local_address.ip_port}"

        def start
            # TODO Currently this server can only accept one connection at at
            # time. Do I want to change that so I can process multiple requests
            # at once?
            Socket.accept_loop(@server) do |connection|

        # Find a value in a line for a given key
        def find_value_for_key(key, document)

            retval = nil

            re = /^#{key} (.*)/
            md = re.match(document)

            if md != nil
                retval = md[1]


        # Parse the document that is sent by the client and convert it into a
        # hash table.
        def parse(document)
            commands =

            message_value = find_value_for_key("MESSAGE", document)
            if message_value == nil then
                $stderr.puts "ERROR: Empty message"
            commands[:message] = message_value

            body_value = find_value_for_key("BODY", document)
            if body_value == nil then
                commands[:body] = "default"
                commands[:body] = body_value


        def handle(connection)
            # TODO Read is going to block until EOF. I need to use something
            # different that will work without an EOF.
            request =

            # The current API will accept a message only from netcat. This
            # message is what the cow will say. Soon I will add support for
            # more features, like choosing your cow.

            # Write back the result of the hash operation
            connection.write process(parse(request))

        def process(commands)
            # TODO Currently I can't capture STDERR output. This is
            # definitely a problem when someone passes a bogus
            # body file name.
            `cowsay -f #{commands[:body]} "#{commands[:message]}"`

server =

There's a few things that I added to this code:

  • Before sending the message to the process method, I now have to parse it.

  • The parse method simply grabs the MESSAGE and BODY values with some help from the find_value_for_key method and then performs some very simple validation.

  • The process method now does some very rudimentaryn parameterization. Eventually I would like some more safeguards in place to ensure that bad input cannot be passed to the cowsay executable, but for now this will do.


First, let's take a look at some "happy path" testing. In your first window, execute the following command:

ruby server.rb
# Returns 'Listening on port 4481'

Great. Now in another window, execute the following command:

ruby client.rb
< this is cool! >
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
< This SUCKS! >
   \         __------~~-,
    \      ,'            ,
          /               \
         /                :
        |                  '
         _| =-.     .-.   ||
         o|/o/       _.   |
         /  ~          \ |
       (____@)  ___~    |
          |_===~~~.`    |
       _______.--~     |
       \________       |
                \      |
              __/-___-- -__
             /            _ \
< Moshi moshi! >
     |      \
     | O . O|

Nice. Let's also try a quick test using netcat:

echo "MESSAGE Oh YEAH\nBODY milk" | nc localhost 4481

...which should return:

< Oh YEAH >
 \     ____________
  \    |__________|
      /           /\
     /           /  \
    |          |     |
    |  ==\ /== |     |
    |   O   O  | \ \ |
    |     <    |  \ \|
   /|          |   \ \
  / |  \_____/ |   / /
 / /|          |  / /|
/||\|          | /||\/
       |  |  |  |
      <__/    \__>

And now for the unhappy path. What happens if I pass a "body type" that the cowsay server doesn't recognize?

echo "MESSAGE Boom goes the dynamite\nBODY bogus" | nc localhost 4481

The client exits normally, but I see the following error message in the console window in which the server is running:

cowsay: Could not find bogus cowfile!

It looks like the STDERR from the cowsay process is only being written to the console. In the future, I'll need to capture that and make the server appropriately.

What if I don't pass a message?

echo "BODY default" | nc localhost 4481

In this case, the client freezes. I then see the following error in the server console window:

ERROR: Empty message

The server then becomes unresponsive. This is definitely the first bug that I will need to fix in my next revision.


I'm happy with the progress of my little socket server and client. In my next revision I am going to focus on the following:

  • Having the server handle bad input gracefully

  • Making sure that the server is able to respond in a predictable, informative way when it experiences issues

  • Finally ditching the backticks and executing the cowsay process in a more robust way.