Tidbits @ Kassemi

A collection of opinions, thoughts, tricks and misc. information.

Monday, June 05, 2006

 

A very quick little http server

I've been working on an AJAX chat room, and I'm trying my best to fine tune it
for system resources. As you know, I use cherrypy frequently, and it's a great
server, but for something as simple as a setInterval poll that just checks
for messages, it's not worth it to have all that extra processing involved. In
fact, it's not even necessary to have something standards compliant... That's
why I decided to write a very simple little web server that can be used within
cherrypy by binding it to another port and mounting a very small application.

The application won't be searched any more than one level, and like I said, there
isn't any support for HTTP standards... It just fires off as many pages per second that
it can (ab -n 10000 http://127.0.0.1:8080/hello/world => 2236.12 #/sec). This compares with a simple CherryPy server on my machine running approx 350 #/sec... Yeah, my machine isn't that great, but it just leads you into finding ways to optimize more than you would ever think.


import SocketServer
import urlparse

STATUS_CODES = {
200: 'OK',
404: 'Not Found',
500: 'Internal Error'}

class Handler(SocketServer.StreamRequestHandler):
header_temp = ''

def end_headers(self):
self.wfile.write(self.header_temp)
self.wfile.write('\r\n')
self.wfile.flush()
self.header_temp = ''

def send_header(self, name, value):
self.header_temp += '%s: %s\r\n' % (name, value)

def send_response(self, code):
self.header_temp = 'HTTP/1.1 %i %s\r\n' % (code, STATUS_CODES[code])

def respond(self, code, ctype, content):
self.send_response(code)
self.send_header('Server', 'TI/1.0')
self.send_header('Content-Type', ctype)
self.end_headers()
self.wfile.write(content)

def path_process(self):
path = self.request_line.split(' ')[1]
parsed = urlparse.urlparse(path)
path = parsed[2]
args = parsed[4]
args = args.replace(';', '&')
arg_dict = dict()
try:
args = [arg_dict.update({item[0]:item[1]}) for item in [arg.split('=') for arg in args.split('&')]]
except: pass
try:
path = path[1:].split('/')
return getattr(apps[path[0]], path[1], False)(**arg_dict)
except: return False

def handle(self):
self.request_line = self.rfile.readline()
try:
res = self.path_process()
except Exception, e:
self.respond(500, 'text/html', 'Internal Server Error
%s' % e)
return
if res:
self.respond(200, 'text/html', res)
else:
self.respond(404, 'text/html', 'Not Found')

class Server(SocketServer.TCPServer): pass
class ServerThreaded(SocketServer.ThreadingMixIn, SocketServer.TCPServer): pass

apps = dict()
def mount(app, name):
global apps
apps[name] = app

def start(host='', port=8080, threaded=False):
if not threaded:
server = Server((host, port), Handler)
else:
server = ServerThreaded((host, port), Handler)
server.serve_forever()

# The sample application:

class Application:
def world(self, *args, **kwargs):
# /hello/world
return 'Hey there!'

def error(self, **kwargs):
# /hello/error
a = 1/0
return 'Error!'

if __name__ == '__main__':
a = Application()
mount(a, 'hello')
start(port=5558)


As always, have fun.... And you people should start making some comments, assuming you're reading... Maybe I should get a counter up on here....

James

Comments: Post a Comment



<< Home

Archives

August 2005   September 2005   October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   April 2006   June 2006   July 2006   August 2006   September 2006   October 2006   November 2006  

This page is powered by Blogger. Isn't yours?