From 08292f21e1a718a814bb2c17a00f78c51d0f96ac Mon Sep 17 00:00:00 2001 From: Cameron Dale Date: Fri, 29 Feb 2008 18:43:41 -0800 Subject: [PATCH] Documented the HTTPServer module. --- apt_dht/HTTPServer.py | 63 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/apt_dht/HTTPServer.py b/apt_dht/HTTPServer.py index b81282c..82a9bab 100644 --- a/apt_dht/HTTPServer.py +++ b/apt_dht/HTTPServer.py @@ -1,4 +1,6 @@ +"""Serve local requests from apt and remote requests from peers.""" + from urllib import unquote_plus from binascii import b2a_hex @@ -11,6 +13,15 @@ from policies import ThrottlingFactory from apt_dht_Khashmir.bencode import bencode class FileDownloader(static.File): + """Modified to make it suitable for apt requests. + + Tries to find requests in the cache. Found files are first checked for + freshness before being sent. Requests for unfound and stale files are + forwarded to the main program for downloading. + + @type manager: L{apt_dht.AptDHT} + @ivar manager: the main program to query + """ def __init__(self, path, manager, defaultType="text/plain", ignoredExts=(), processors=None, indexNames=None): self.manager = manager @@ -43,6 +54,13 @@ class FileDownloader(static.File): self.processors, self.indexNames[:]) class FileUploaderStream(stream.FileStream): + """Modified to make it suitable for streaming to peers. + + Streams the file is small chunks to make it easier to throttle the + streaming to peers. + + @ivar CHUNK_SIZE: the size of chunks of data to send at a time + """ CHUNK_SIZE = 4*1024 @@ -54,6 +72,8 @@ class FileUploaderStream(stream.FileStream): if length == 0: self.f = None return None + + # Remove the SendFileBuffer and mmap use, just use string reads and writes readSize = min(length, self.CHUNK_SIZE) @@ -69,12 +89,18 @@ class FileUploaderStream(stream.FileStream): class FileUploader(static.File): + """Modified to make it suitable for peer requests. + + Uses the modified L{FileUploaderStream} to stream the file for throttling, + and doesn't do any listing of directory contents. + """ def render(self, req): if not self.fp.exists(): return responsecode.NOT_FOUND if self.fp.isdir(): + # Don't try to render a directory listing return responsecode.NOT_FOUND try: @@ -89,6 +115,7 @@ class FileUploader(static.File): raise response = http.Response() + # Use the modified FileStream response.stream = FileUploaderStream(f, 0, self.fp.getsize()) for (header, value) in ( @@ -101,15 +128,38 @@ class FileUploader(static.File): return response class TopLevel(resource.Resource): + """The HTTP server for all requests, both from peers and apt. + + @type directory: L{twisted.python.filepath.FilePath} + @ivar directory: the directory to check for cached files + @type db: L{db.DB} + @ivar db: the database to use for looking up files and hashes + @type manager: L{apt_dht.AptDHT} + @ivar manager: the main program object to send requests to + @type factory: L{twisted.web2.channel.HTTPFactory} or L{policies.ThrottlingFactory} + @ivar factory: the factory to use to server HTTP requests + + """ + addSlash = True def __init__(self, directory, db, manager): + """Initialize the instance. + + @type directory: L{twisted.python.filepath.FilePath} + @param directory: the directory to check for cached files + @type db: L{db.DB} + @param db: the database to use for looking up files and hashes + @type manager: L{apt_dht.AptDHT} + @param manager: the main program object to send requests to + """ self.directory = directory self.db = db self.manager = manager self.factory = None def getHTTPFactory(self): + """Initialize and get the factory for this HTTP server.""" if self.factory is None: self.factory = channel.HTTPFactory(server.Site(self), **{'maxPipeline': 10, @@ -118,6 +168,7 @@ class TopLevel(resource.Resource): return self.factory def render(self, ctx): + """Render a web page with descriptive statistics.""" return http.Response( 200, {'content-type': http_headers.MimeType('text', 'html')}, @@ -126,31 +177,41 @@ class TopLevel(resource.Resource):

TODO: eventually some stats will be shown here.""") def locateChild(self, request, segments): + """Process the incoming request.""" log.msg('Got HTTP request for %s from %s' % (request.uri, request.remoteAddr)) name = segments[0] + + # If the request is for a shared file (from a peer) if name == '~': if len(segments) != 2: log.msg('Got a malformed request from %s' % request.remoteAddr) return None, () + + # Find the file in the database hash = unquote_plus(segments[1]) files = self.db.lookupHash(hash) if files: + # If it is a file, return it if 'path' in files[0]: log.msg('Sharing %s with %s' % (files[0]['path'].path, request.remoteAddr)) return FileUploader(files[0]['path'].path), () else: + # It's not for a file, but for a piece string, so return that log.msg('Sending torrent string %s to %s' % (b2a_hex(hash), request.remoteAddr)) return static.Data(bencode({'t': files[0]['pieces']}), 'application/x-bencoded'), () else: log.msg('Hash could not be found in database: %s' % hash) - + + # Only local requests (apt) get past this point if request.remoteAddr.host != "127.0.0.1": log.msg('Blocked illegal access to %s from %s' % (request.uri, request.remoteAddr)) return None, () if len(name) > 1: + # It's a request from apt return FileDownloader(self.directory.path, self.manager), segments[0:] else: + # Will render the statistics page return self, () log.msg('Got a malformed request for "%s" from %s' % (request.uri, request.remoteAddr)) -- 2.39.5