HTTPHandler.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. # Written by Bram Cohen
  2. # see LICENSE.txt for license information
  3. from cStringIO import StringIO
  4. from sys import stdout
  5. import time
  6. from clock import clock
  7. from gzip import GzipFile
  8. try:
  9. True
  10. except:
  11. True = 1
  12. False = 0
  13. DEBUG = False
  14. weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  15. months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
  16. 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
  17. class HTTPConnection:
  18. def __init__(self, handler, connection):
  19. self.handler = handler
  20. self.connection = connection
  21. self.buf = ''
  22. self.closed = False
  23. self.done = False
  24. self.donereading = False
  25. self.next_func = self.read_type
  26. def get_ip(self):
  27. return self.connection.get_ip()
  28. def data_came_in(self, data):
  29. if self.donereading or self.next_func is None:
  30. return True
  31. self.buf += data
  32. while True:
  33. try:
  34. i = self.buf.index('\n')
  35. except ValueError:
  36. return True
  37. val = self.buf[:i]
  38. self.buf = self.buf[i+1:]
  39. self.next_func = self.next_func(val)
  40. if self.donereading:
  41. return True
  42. if self.next_func is None or self.closed:
  43. return False
  44. def read_type(self, data):
  45. self.header = data.strip()
  46. words = data.split()
  47. if len(words) == 3:
  48. self.command, self.path, garbage = words
  49. self.pre1 = False
  50. elif len(words) == 2:
  51. self.command, self.path = words
  52. self.pre1 = True
  53. if self.command != 'GET':
  54. return None
  55. else:
  56. return None
  57. if self.command not in ('HEAD', 'GET'):
  58. return None
  59. self.headers = {}
  60. return self.read_header
  61. def read_header(self, data):
  62. data = data.strip()
  63. if data == '':
  64. self.donereading = True
  65. if self.headers.get('accept-encoding','').find('gzip') > -1:
  66. self.encoding = 'gzip'
  67. else:
  68. self.encoding = 'identity'
  69. r = self.handler.getfunc(self, self.path, self.headers)
  70. if r is not None:
  71. self.answer(r)
  72. return None
  73. try:
  74. i = data.index(':')
  75. except ValueError:
  76. return None
  77. self.headers[data[:i].strip().lower()] = data[i+1:].strip()
  78. if DEBUG:
  79. print data[:i].strip() + ": " + data[i+1:].strip()
  80. return self.read_header
  81. def answer(self, (responsecode, responsestring, headers, data)):
  82. if self.closed:
  83. return
  84. if self.encoding == 'gzip':
  85. compressed = StringIO()
  86. gz = GzipFile(fileobj = compressed, mode = 'wb', compresslevel = 9)
  87. gz.write(data)
  88. gz.close()
  89. cdata = compressed.getvalue()
  90. if len(cdata) >= len(data):
  91. self.encoding = 'identity'
  92. else:
  93. if DEBUG:
  94. print "Compressed: %i Uncompressed: %i\n" % (len(cdata),len(data))
  95. data = cdata
  96. headers['Content-Encoding'] = 'gzip'
  97. # i'm abusing the identd field here, but this should be ok
  98. if self.encoding == 'identity':
  99. ident = '-'
  100. else:
  101. ident = self.encoding
  102. self.handler.log( self.connection.get_ip(), ident, '-',
  103. self.header, responsecode, len(data),
  104. self.headers.get('referer','-'),
  105. self.headers.get('user-agent','-') )
  106. self.done = True
  107. r = StringIO()
  108. r.write('HTTP/1.0 ' + str(responsecode) + ' ' +
  109. responsestring + '\r\n')
  110. if not self.pre1:
  111. headers['Content-Length'] = len(data)
  112. for key, value in headers.items():
  113. r.write(key + ': ' + str(value) + '\r\n')
  114. r.write('\r\n')
  115. if self.command != 'HEAD':
  116. r.write(data)
  117. self.connection.write(r.getvalue())
  118. if self.connection.is_flushed():
  119. self.connection.shutdown(1)
  120. class HTTPHandler:
  121. def __init__(self, getfunc, minflush):
  122. self.connections = {}
  123. self.getfunc = getfunc
  124. self.minflush = minflush
  125. self.lastflush = clock()
  126. def external_connection_made(self, connection):
  127. self.connections[connection] = HTTPConnection(self, connection)
  128. def connection_flushed(self, connection):
  129. if self.connections[connection].done:
  130. connection.shutdown(1)
  131. def connection_lost(self, connection):
  132. ec = self.connections[connection]
  133. ec.closed = True
  134. del ec.connection
  135. del ec.next_func
  136. del self.connections[connection]
  137. def data_came_in(self, connection, data):
  138. c = self.connections[connection]
  139. if not c.data_came_in(data) and not c.closed:
  140. c.connection.shutdown(1)
  141. def log(self, ip, ident, username, header,
  142. responsecode, length, referrer, useragent):
  143. year, month, day, hour, minute, second, a, b, c = time.localtime(time.time())
  144. print '%s %s %s [%02d/%3s/%04d:%02d:%02d:%02d] "%s" %i %i "%s" "%s"' % (
  145. ip, ident, username, day, months[month], year, hour,
  146. minute, second, header, responsecode, length, referrer, useragent)
  147. t = clock()
  148. if t - self.lastflush > self.minflush:
  149. self.lastflush = t
  150. stdout.flush()