| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- # The contents of this file are subject to the BitTorrent Open Source License
- # Version 1.1 (the License). You may not copy or use this file, in either
- # source code or executable form, except in compliance with the License. You
- # may obtain a copy of the License at http://www.bittorrent.com/license/.
- #
- # Software distributed under the License is distributed on an AS IS basis,
- # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- # for the specific language governing rights and limitations under the
- # License.
- # Written by Bram Cohen
- import datetime
- from RawServer_twisted import Handler
- from cStringIO import StringIO
- from sys import stdout
- import time
- from gzip import GzipFile
- from BTL.translation import _
- DEBUG = False
- class HTTPConnector(object):
- def __init__(self, handler, connection):
- self.handler = handler
- self.connection = connection
- self.buf = ''
- self.closed = False
- self.done = False
- self.donereading = False
- self.next_func = self.read_type
- def get_ip(self):
- return self.connection.ip
- def data_came_in(self, data):
- if self.donereading or self.next_func is None:
- return True
- self.buf += data
- while True:
- try:
- i = self.buf.index('\n')
- except ValueError:
- return True
- val = self.buf[:i]
- self.buf = self.buf[i+1:]
- self.next_func = self.next_func(val)
- if self.donereading:
- return True
- if self.next_func is None or self.closed:
- return False
- def read_type(self, data):
- self.header = data.strip()
- words = data.split()
- if len(words) == 3:
- self.command, self.path, garbage = words
- self.pre1 = False
- elif len(words) == 2:
- self.command, self.path = words
- self.pre1 = True
- if self.command != 'GET':
- return None
- else:
- return None
- if self.command not in ('HEAD', 'GET'):
- return None
- self.headers = {}
- return self.read_header
- def read_header(self, data):
- data = data.strip()
- if data == '':
- self.donereading = True
- # check for Accept-Encoding: header, pick a
- if self.headers.has_key('accept-encoding'):
- ae = self.headers['accept-encoding']
- if DEBUG:
- print "Got Accept-Encoding: " + ae + "\n"
- else:
- #identity assumed if no header
- ae = 'identity'
- # this eventually needs to support multple acceptable types
- # q-values and all that fancy HTTP crap
- # for now assume we're only communicating with our own client
- if ae.find('gzip') != -1:
- self.encoding = 'gzip'
- else:
- #default to identity.
- self.encoding = 'identity'
- r = self.handler.getfunc(self, self.path, self.headers)
- if r is not None:
- self.answer(r)
- return None
- try:
- i = data.index(':')
- except ValueError:
- return None
- self.headers[data[:i].strip().lower()] = data[i+1:].strip()
- if DEBUG:
- print data[:i].strip() + ": " + data[i+1:].strip()
- return self.read_header
- def answer(self, (responsecode, responsestring, headers, data)):
- if self.closed:
- return
- if self.encoding == 'gzip':
- #transform data using gzip compression
- #this is nasty but i'm unsure of a better way at the moment
- compressed = StringIO()
- gz = GzipFile(fileobj = compressed, mode = 'wb', compresslevel = 9)
- gz.write(data)
- gz.close()
- compressed.seek(0,0)
- cdata = compressed.read()
- compressed.close()
- if len(cdata) >= len(data):
- self.encoding = 'identity'
- else:
- if DEBUG:
- print _("Compressed: %i Uncompressed: %i\n") % (len(cdata),len(data))
- data = cdata
- headers['Content-Encoding'] = 'gzip'
- # i'm abusing the identd field here, but this should be ok
- if self.encoding == 'identity':
- ident = '-'
- else:
- ident = self.encoding
- username = '-'
- referer = self.headers.get('referer','-')
- useragent = self.headers.get('user-agent','-')
- timestamp = datetime.datetime.utcnow().isoformat()
- if DEBUG:
- print '%s %s %s [%s] "%s" %i %i "%s" "%s"' % (
- self.connection.ip, ident, username, timestamp, self.header,
- responsecode, len(data), referer, useragent)
- t = time.time()
- if t - self.handler.lastflush > self.handler.minflush:
- self.handler.lastflush = t
- stdout.flush()
- self.done = True
- r = 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(Handler):
- def __init__(self, getfunc, minflush):
- self.connections = {}
- self.getfunc = getfunc
- self.minflush = minflush
- self.lastflush = time.time()
- def connection_made(self, connection):
- if DEBUG:
- print "HTTPHandler.connection_made"
- self.connections[connection] = HTTPConnector(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 = True
- del ec.connection
- del ec.next_func
- del self.connections[connection]
- def data_came_in(self, connection, data):
- if DEBUG:
- print "HTTPHandler.data_came_in: '%s'" % data
- c = self.connections[connection]
- if not c.data_came_in(data) and not c.closed:
- c.connection.shutdown(1)
|