Just to clarify up front that this is just a learning project and I have no intention of using this in production. There are several very good Python application servers out there already. But I am trying to learn more about concurrency, so I set out to write one of the things (I thought) I knew.
Also, because I wanted to "closer to the metal" so I started out with just Socket and want to keep it that way.
Below is the important parts of what I have so far. self.iq is a Queue object (inbound_queue) which then does nothing really, but puts the request (which includes the socket object) into the outbound_queue and then a Consumer object takes the request from the outbound_queue and passes it to the ResponseHandler. This seems to work fine with just me hitting it but I am concerned that I am opening myself up to a race condition with a naive implementation. Specifically assigning things to the ServerClass object that are request specific.
So the question is: Is there a better way to do this, or does my Queue usage prevent two threads from picking up and operating on the same object? Should I be encapsulating things like the WSGI environment stuff into a separate object that can be also passed into the queue? Doing stuff like that gets tricky in trying to write a server that is WSGI compliant because of the need to pass in callback functions.
class Consumer(threading.Thread):
def __init__(self, out_queue, server):
threading.Thread.__init__(self)
self.out_queue = out_queue
self.server = server
def run(self):
while True:
item = self.out_queue.get()
self.server.ResponseHandler(self.server, item)
self.out_queue.task_done()
class QueueConsumerServer(object):
methods_allowed = ['get', 'post', 'put', 'patch', 'delete', 'options', 'upgrade']
def __init__(self, host, port, application):
self.host = host
self.port = port
self.application = application
self.iq = Queue.Queue()
self.oq = Queue.Queue()
self.socket = socket.socket()
self.socket.bind((self.host, self.port))
#<snip of lots of assigning stuff to environ>
self.environ = environ
headers_set = []
headers_sent = []
for i in xrange(3):
thr = Producer(self.iq, self.oq)
thr.daemon = True
thr.start()
for i in xrange(3):
thr = Consumer(self.oq, self)
thr.daemon = True
thr.start()
def handle_request(self):
self.socket.listen(1)
try:
while True:
cli, addr = self.socket.accept()
data = cli.recv(1024)
request_data = self.parse_request_data(data)
req.path = request_data[1]
req.cli = cli
self.iq.put(req)
return
except Exception, ex:
print 'e', ex,
sys.exit(1)
finally:
sys.stdout.flush()
self.socket.close()