Telematica: minor bug fixes.

parent 10ebfad0
......@@ -110,9 +110,8 @@ class ClientConnection(object, AsyncBase, asyncore.dispatcher):
try:
chunk = self.recv(1)
except socket.error, e:
# Suppress "Resource temporarily unavailable" exceptions.
# Handle "Resource temporarily unavailable" exceptions.
if e.errno == EAGAIN:
# Wait 5 ms
import time
time.sleep(0.005)
continue
......@@ -207,7 +206,7 @@ class ClientConnection(object, AsyncBase, asyncore.dispatcher):
Helper function used to parse the server notifications. This way,
message parsing is separated from the notification logic.
"""
match = re.match('^SAY ([^\r\n:/]+)/(.+)', buf)
match = re.match('^SAY ([^\x00-\x1F:/]+)/([^\x00-\x1F]+)', buf)
if match:
if 'message' in self.event_list:
self.event_list['message'](self, match.group(1), match.group(2))
......
......@@ -32,7 +32,8 @@ 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 various event callback functions:
event-oriented and provides the following event callback functions (among some
other less relevant ones):
1. **handle_write**.
......@@ -76,6 +77,12 @@ 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)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -191,5 +198,11 @@ We implemented the following commands:
Server side
----------
Foo
Multiple client handling
~~~~~~~~~~~~~~~~~~~~~~~~
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*.
......@@ -32,6 +32,9 @@ class Server(asyncore.dispatcher):
self.port = port
self.handler = handler
# Dictonary which maps ip/port tuple to client data object.
self.clients = {}
logging.config.fileConfig('logging.conf')
self.log = logging.getLogger('Server')
......@@ -42,9 +45,6 @@ class Server(asyncore.dispatcher):
self.log.info('waiting for incoming connections on port %s.' % port)
self.listen(5)
# Dictonary which maps ip/port tuple to client data object.
self.clients = {}
def handle_accept(self):
"""
Handle the incoming connection's request.
......@@ -149,12 +149,27 @@ class RequestHandler(AsyncBase, asyncore.dispatcher):
self.parse_response(buf)
def handle_error(self):
import sys
t, v, tb = sys.exc_info()
tbinfo = []
while tb:
tbinfo.append((
tb.tb_frame.f_code.co_filename,
tb.tb_frame.f_code.co_name,
str(tb.tb_lineno)
))
tb = tb.tb_next
file, function, line = tbinfo[-1]
self.log.error('%s raised "%s" in %s:%s' \
% (str(v), function, file, line))
self.log.error(' '.join(['[%s|%s|%s]' % x for x in tbinfo]))
self.server.disconnect_client(self.address)
def parse_response(self, buf):
"""
>>> class DummyServer(object):
... def __init__(self): self.log = None
... def __init__(self): self.log = None; self.clients = {}
... def change_username(self, addr, username): pass
... def send_all(self, msg, sender): pass
>>> req = RequestHandler(None, None, None, DummyServer())
......@@ -171,14 +186,20 @@ class RequestHandler(AsyncBase, asyncore.dispatcher):
# Client wants to chat.
return self.send_positive('Ok')
if cmd == 'USER':
# User changes/sets its username.
if re.match(ur'^[^\u0000-\u001f\u007f-\u009f/:]+$', buf[5:]):
# User changes/sets its username. Make sure the username is not
# already taken. If the name is valid, notify the other clients.
name = buf[5:]
for c in self.server.clients:
if self.server.clients[c].username == name \
and self.server.clients[c].handler != self:
return self.send_negative('Username already taken.')
if re.match(ur'^[^\u0000-\u001f\u007f-\u009f/:]+$', name):
if not self.username:
self.send_all('JOIN %s' % buf[5:], True)
self.send_all('JOIN %s' % name, True)
else:
self.send_all('RENAME %s/%s' \
% (self.username, buf[5:]), True)
self.set_username(buf[5:])
% (self.username, name), True)
self.set_username(name)
return self.send_positive('Ok')
return self.send_negative('Invalid username.')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment