Blame | Last modification | View Log | Download
# Written by Bram Cohen# see LICENSE.txt for license informationfrom cStringIO import StringIOfrom sys import stdoutimport timefrom clock import clockfrom gzip import GzipFiletry:Trueexcept:True = 1False = 0DEBUG = Falseweekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']class HTTPConnection:def __init__(self, handler, connection):self.handler = handlerself.connection = connectionself.buf = ''self.closed = Falseself.done = Falseself.donereading = Falseself.next_func = self.read_typedef get_ip(self):return self.connection.get_ip()def data_came_in(self, data):if self.donereading or self.next_func is None:return Trueself.buf += datawhile True:try:i = self.buf.index('\n')except ValueError:return Trueval = self.buf[:i]self.buf = self.buf[i+1:]self.next_func = self.next_func(val)if self.donereading:return Trueif self.next_func is None or self.closed:return Falsedef read_type(self, data):self.header = data.strip()words = data.split()if len(words) == 3:self.command, self.path, garbage = wordsself.pre1 = Falseelif len(words) == 2:self.command, self.path = wordsself.pre1 = Trueif self.command != 'GET':return Noneelse:return Noneif self.command not in ('HEAD', 'GET'):return Noneself.headers = {}return self.read_headerdef read_header(self, data):data = data.strip()if data == '':self.donereading = Trueif self.headers.get('accept-encoding','').find('gzip') > -1:self.encoding = 'gzip'else:self.encoding = 'identity'r = self.handler.getfunc(self, self.path, self.headers)if r is not None:self.answer(r)return Nonetry:i = data.index(':')except ValueError:return Noneself.headers[data[:i].strip().lower()] = data[i+1:].strip()if DEBUG:print data[:i].strip() + ": " + data[i+1:].strip()return self.read_headerdef answer(self, (responsecode, responsestring, headers, data)):if self.closed:returnif self.encoding == 'gzip':compressed = StringIO()gz = GzipFile(fileobj = compressed, mode = 'wb', compresslevel = 9)gz.write(data)gz.close()cdata = compressed.getvalue()if len(cdata) >= len(data):self.encoding = 'identity'else:if DEBUG:print "Compressed: %i Uncompressed: %i\n" % (len(cdata),len(data))data = cdataheaders['Content-Encoding'] = 'gzip'# i'm abusing the identd field here, but this should be okif self.encoding == 'identity':ident = '-'else:ident = self.encodingself.handler.log( self.connection.get_ip(), ident, '-',self.header, responsecode, len(data),self.headers.get('referer','-'),self.headers.get('user-agent','-') )self.done = Truer = StringIO()r.write('HTTP/1.0 ' + str(responsecode) + ' ' +responsestring + '\r\n')if not self.pre1:headers['Content-Length'] = len(data)for key, value in headers.items():r.write(key + ': ' + str(value) + '\r\n')r.write('\r\n')if self.command != 'HEAD':r.write(data)self.connection.write(r.getvalue())if self.connection.is_flushed():self.connection.shutdown(1)class HTTPHandler:def __init__(self, getfunc, minflush):self.connections = {}self.getfunc = getfuncself.minflush = minflushself.lastflush = clock()def external_connection_made(self, connection):self.connections[connection] = HTTPConnection(self, connection)def connection_flushed(self, connection):if self.connections[connection].done:connection.shutdown(1)def connection_lost(self, connection):ec = self.connections[connection]ec.closed = Truedel ec.connectiondel ec.next_funcdel self.connections[connection]def data_came_in(self, connection, data):c = self.connections[connection]if not c.data_came_in(data) and not c.closed:c.connection.shutdown(1)def log(self, ip, ident, username, header,responsecode, length, referrer, useragent):year, month, day, hour, minute, second, a, b, c = time.localtime(time.time())print '%s %s %s [%02d/%3s/%04d:%02d:%02d:%02d] "%s" %i %i "%s" "%s"' % (ip, ident, username, day, months[month], year, hour,minute, second, header, responsecode, length, referrer, useragent)t = clock()if t - self.lastflush > self.minflush:self.lastflush = tstdout.flush()