
Implementation details
======================

Client side
----------

The client side of our chat system is generally divided into two parts:

 1. **Connection to server.** We use an asynchronous connection to the server
    (and also for the server itself to handle the clients; see "Server side"
    section) to send and receive messages from/to the chat server.

 2. **User interface for user.** We use a text-based user interface built with
    the python bindings for the curses library, which is the de-facto standard
    for portable advanced terminal handling.

The user interface depends on the client connection and the connection itself is
a completely independent component. Due to its event mechanism (see below), the
client connection can easily be used for different user interfaces.

Client chat connection
~~~~~~~~~~~~~~~~~~~~~~

First, we implemented the chat connection and we built the user interface on
top of the connection. This way, we're able to test the connection with the
test server provided by the assignment. Otherwise, debugging the connection is
much harder; text-based interface means the entire console is used to display
the interface, so it's not possible to report any exceptions to stdout.

Since some functionality of the chat connection is used in both the client and
server, it is wise to create a base class containing those methods: *AsyncBase*.
We used the *asyncore.dispatcher* class (part of the python standard library) to
create a nice abstraction layer on top of the socket. The abstraction layer is
event-oriented and provides the following event callback functions (among some
other less relevant ones):

 1. **handle_write**.

 2. **handle_read**.

 3. **handle_connect**.

 4. **handle_close**.

Because those function callbacks have self-documenting names, the callbacks are
not further discussed here (for more information about those callbacks, see the
implementation in *async.py* and *asyncbase.py*). This event callback mechanism
allowed us to easily build an interactive user interface on top of the
connection. Those various callback functions will handle the connection-specific
logic and triggers a new UI event based on the occurred event.

For example, if an authentication response is sent to the client (after the
client has sent **USER foo\\r\\n** to the server), the function **handle_read()** is
called. This callback will parse the response and, if the authentication is
successful, trigger an *authenticated* UI event (which will be received by the
text-based user interface). This event mechanism allows a developer to build
different user interfaces on top to a client connection, instead of developing
the response handling over and over again.

The connection runs in a separated thread from the user interface thread. The
reason behind this decision is based on experiences with user interface; when an
user presses a key (and holds it), the connection will not respond until the
user releases the key pressed. If the user interface is separated into a
different thread, the connection is able to respond (due to context switches)
and the user can presses a key as long as he wants. Note: there are different
ways to deal with this problem, but this is a solid solution and it is easy to
implement.

In order to send chat messages to the chat server in a thread-safe manner, we
used a synchronized queue as message buffer, since we're using the two threads
(one for the UI and one for the connection to the server). This synchronized
queue is part of the python standard library and called Queue.Queue. The user
interface adds its messages to the message queue and the client connection reads
the messages from the queue in FIFO (first-in, first-out) order. The client
connection does take messages from the queue, if its local message buffer is
empty (which means all its data is sent to the server or the connection is
just initialised).

The protocol uses the Backus-Naur form and therefore, we chose regular
expressions to validate incoming messages from the server. Regexes are easier to
maintain than handwritten parsers, since they allow you to check for certain
ranges of characters (for example, the control character range is
**[\\x00-\\x1F]**).

Text-based interface (using curses)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The designed text-based user interface consists of four major components:

 1. **Main chat window.** This window displays all notifications sent from the
    server and will show all chat messages that have been sent or received. This
    window is shown at the top of the interface.

 2. **Debug log window.** Information regarding the chat protocol, incoming and
    outgoing protocol messages and raised exceptions are displayed in this
    window. This window is displayed below the main chat window and above the
    indicator bar.

 3. **Indicator bar.** When the client raises an exception, the indicator bar
    will display the exception's error code and message to the user. The
    indicator bar does also show the connectivity (online, when connected to a
    server, or offline) and if connected to a chat server, the currently used
    nickname.  This bar is shown below the debug log window and above the
    command bar.

 4. **Command bar.** The user uses this bar to control the chat client (using
    special commands; see section "User interface commands" for these commands)
    and send chat messages to the server. This command bar is shown at the
    bottom of the interface.

The main chat window and debug log window have a common base class called
*BaseWindow*. This base class contains the initialisation of the curses window
object using the function *curses.newwin(height, width, top, left)*. The base
class takes care of redrawing the window object. Redrawing is necessary to
display new information (if a new line is appended to the window, the other
lines should move one line up) and hide old information (if there are more lines
than the window object's height).

The indicator bar and command bar have a common base class called *BaseBar*.
This base class initialises the window object of this bar similar to the
*BaseWindow* class (except that the height is always :math:`1`, see below why).
It also contains functionality to change the information currently displayed at
the bar and it allows to specify a prefix (which will remain the same if the
message of the bar is modified). The prefix is invented to simplify changing
the indicator bar (for example, to display an error message, if a non-fatal
error occurs) but prevent the change from overwriting all information shown at
the indicator bar (e.g. nickname and currently connected server).

We used a simple form of window composition. First, get the horizontal and
vertical maximum amount of characters the terminal can display, using the screen
object's function *getmaxyx()* (the y and x axis are swapped for historical
reasons). The height of a bar is one character row. Therefore, the command bar
at the bottom takes one row and the indicator bar above the command bar takes
another row. Above the indicator bar is the debug window, which height is five
rows (we used this fixed value because it worked well during the development of
the chat client). To separate the content of the debug window from the content
of the main chat window, we added an empty row between the two windows. After
obtaining the size of the terminal, the chat window's height is set to
:math:`y_{max} - 8` (where :math:`8` is the sum of the two bars, an empty row
and the debug window). The width of the windows and bars is set to
:math:`x_{max}`.

User interface commands
~~~~~~~~~~~~~~~~~~~~~~~

In order to control the chat client, we implemented some features which can be
executed using a special command. To execute a special command, you should start
your chat message with a forward-slash directly followed by the command to
execute. The forward-slash should be the first character of the command bar,
otherwise the command is not recognised (the slash distinguishes chat messages
from commands to execute).

We implemented the following commands:

 * **/close** Close visible chat window or the help screen. In the future,
   this will allow you to connect to multiple chat server and close an
   individual chat screen (for example, when private messaging is implemented).
   For now, this command will close the help screen or if no help screen is
   displayed, shutdown the chat client application.

 * **/connect** Connect to a chat server using **HOSTNAME** and optionally a
   **PORT**.  If no **PORT** is specified, the default port 16897 is used. This
   command will first disconnect from any currently connected chat server.

   Synopsis: **/connect HOSTNAME [PORT]**

   For example, **/connect ow150** will connect to the provided test server of
   this assignment (when you're using the UvA network).

 * **/disconnect** Disconnect from the currently connected chat server. If you
   are not connected to a chat server, this command will do nothing.

 * **/exec** Execute python code. All following arguments are interpreted by the
   python interpreter as if it was real python code. Therefore, you should pay
   attention to the syntax and semantics. You can use the variable **main** to
   access all other parts of the chat client (since it is the CLI instance).

   For example, to list all variables and function of the CLI, type: **/exec
   print dir(main)**.

 * **/help** This help page.

 * **/names** Retrieve and display a list of user names currently logged in on
   the chat server.

 * **/quit** Quit this chat application (shortcut: ^c).

 * **/raw** Send a raw command to the server. This is useful during
   debugging the client and/or server since you do not have to restart the
   application to send an unimplemented message to the server/client. All
   following arguments are sent as a literal string to the server or client.

 * **/user** Change your user name to **USER**.

   Synopsis: **/user USER**

Server side
----------

The class *Server* accepts the incoming connections from clients and initialises
a request handler (see class *RequestHandler*) for each accepted connection. The
servers logs all its information, warnings and errors to stdout. To change the
behaviour of the server log, alter the configuration file *logging.conf*.

In order to create a list of currently active users, we used an dictionary based
on the client's address tuple (ip address, port). To check if a user does not
take the nickname of an existing user, we simply iterate over the list of active
users and compare the name of each user with the requested nickname. If it
exists, return a negative response. Otherwise, if the nickname is valid according
to the protocol, return a positive response. It is possible to build an index
for the name lookup, but that optimization is not worth it as long as there
aren't that many clients connected. And if there are many clients connected, there
are more important scalability problems to solve (e.g. better socket
multiplexing, synchronisation between different server and load balancing).

Compared to the client (and its interface), the server is very simplistic. This
is on purpose, because we wanted to create a server which does exactly what is
required in the protocol specification. In this case, less is more.

