X-Git-Url: https://git.mxchange.org/?p=quix0rs-apt-p2p.git;a=blobdiff_plain;f=apt_dht%2FHTTPServer.py;h=53755478d20f9e7f737003d824e06053b963060e;hp=0e53c237ef175bdca4934bf335b05300ca8e337b;hb=d354e6695046640a6e0dc2f640826caad9a35b3d;hpb=8297bc9a2aa8132ea1a7363761d9d5c73a1efca2 diff --git a/apt_dht/HTTPServer.py b/apt_dht/HTTPServer.py index 0e53c23..5375547 100644 --- a/apt_dht/HTTPServer.py +++ b/apt_dht/HTTPServer.py @@ -1,9 +1,15 @@ +from urllib import unquote_plus +from binascii import b2a_hex + from twisted.python import log from twisted.internet import defer -from twisted.web2 import server, http, resource, channel +from twisted.web2 import server, http, resource, channel, stream from twisted.web2 import static, http_headers, responsecode +from policies import ThrottlingFactory +from apt_dht_Khashmir.bencode import bencode + class FileDownloader(static.File): def __init__(self, path, manager, defaultType="text/plain", ignoredExts=(), processors=None, indexNames=None): @@ -25,10 +31,10 @@ class FileDownloader(static.File): if self.manager: path = 'http:/' + req.uri if resp.code >= 200 and resp.code < 400: - return self.manager.check_freshness(path, resp.headers.getHeader('Last-Modified'), resp) + return self.manager.check_freshness(req, path, resp.headers.getHeader('Last-Modified'), resp) log.msg('Not found, trying other methods for %s' % req.uri) - return self.manager.get_resp(path) + return self.manager.get_resp(req, path) return resp @@ -36,22 +42,81 @@ class FileDownloader(static.File): return self.__class__(path, self.manager, self.defaultType, self.ignoredExts, self.processors, self.indexNames[:]) - +class FileUploaderStream(stream.FileStream): + + CHUNK_SIZE = 4*1024 + + def read(self, sendfile=False): + if self.f is None: + return None + + length = self.length + if length == 0: + self.f = None + return None + + readSize = min(length, self.CHUNK_SIZE) + + self.f.seek(self.start) + b = self.f.read(readSize) + bytesRead = len(b) + if not bytesRead: + raise RuntimeError("Ran out of data reading file %r, expected %d more bytes" % (self.f, length)) + else: + self.length -= bytesRead + self.start += bytesRead + return b + + +class FileUploader(static.File): + + def render(self, req): + if not self.fp.exists(): + return responsecode.NOT_FOUND + + if self.fp.isdir(): + return responsecode.NOT_FOUND + + try: + f = self.fp.open() + except IOError, e: + import errno + if e[0] == errno.EACCES: + return responsecode.FORBIDDEN + elif e[0] == errno.ENOENT: + return responsecode.NOT_FOUND + else: + raise + + response = http.Response() + response.stream = FileUploaderStream(f, 0, self.fp.getsize()) + + for (header, value) in ( + ("content-type", self.contentType()), + ("content-encoding", self.contentEncoding()), + ): + if value is not None: + response.headers.setHeader(header, value) + + return response + class TopLevel(resource.Resource): addSlash = True - def __init__(self, directory, manager): + def __init__(self, directory, db, manager): self.directory = directory + self.db = db self.manager = manager - self.subdirs = {} - - def setDirectories(self, dirs): - self.subdirs = {} - for k in dirs: - # Don't allow empty subdirectory - if k: - self.subdirs[k] = dirs[k] - + self.factory = None + + def getHTTPFactory(self): + if self.factory is None: + self.factory = channel.HTTPFactory(server.Site(self), + **{'maxPipeline': 10, + 'betweenRequestsTimeOut': 60}) + self.factory = ThrottlingFactory(self.factory, writeLimit = 30*1024) + return self.factory + def render(self, ctx): return http.Response( 200, @@ -61,10 +126,23 @@ class TopLevel(resource.Resource):

TODO: eventually some stats will be shown here.""") def locateChild(self, request, segments): + log.msg('Got HTTP request for %s from %s' % (request.uri, request.remoteAddr)) name = segments[0] - if name in self.subdirs: - log.msg('Sharing %s with %s' % (request.uri, request.remoteAddr)) - return static.File(self.subdirs[name].path), segments[1:] + if name == '~': + if len(segments) != 2: + log.msg('Got a malformed request from %s' % request.remoteAddr) + return None, () + hash = unquote_plus(segments[1]) + files = self.db.lookupHash(hash) + if files: + if 'path' in files[0]: + log.msg('Sharing %s with %s' % (files[0]['path'].path, request.remoteAddr)) + return FileUploader(files[0]['path'].path), () + else: + log.msg('Sending torrent string %s to %s' % (b2a_hex(hash), request.remoteAddr)) + return static.Data(bencode({'t': files[0]['pieces']}), 'application/bencoded'), () + else: + log.msg('Hash could not be found in database: %s' % hash) if request.remoteAddr.host != "127.0.0.1": log.msg('Blocked illegal access to %s from %s' % (request.uri, request.remoteAddr)) @@ -75,15 +153,29 @@ class TopLevel(resource.Resource): else: return self, () + log.msg('Got a malformed request for "%s" from %s' % (request.uri, request.remoteAddr)) + return None, () + if __name__ == '__builtin__': - # Running from twistd -y - t = TopLevel('/home', None) - t.addDirectory('/tmp') - t.addDirectory('/var/log') - site = server.Site(t) + # Running from twistd -ny HTTPServer.py + # Then test with: + # wget -S 'http://localhost:18080/~/whatever' + # wget -S 'http://localhost:18080/~/pieces' + + import os.path + from twisted.python.filepath import FilePath + + class DB: + def lookupHash(self, hash): + if hash == 'pieces': + return [{'pieces': 'abcdefghij0123456789\xca\xec\xb8\x0c\x00\xe7\x07\xf8~])\x8f\x9d\xe5_B\xff\x1a\xc4!'}] + return [{'path': FilePath(os.path.expanduser('~/school/optout'))}] + + t = TopLevel(FilePath(os.path.expanduser('~')), DB(), None) + factory = t.getHTTPFactory() # Standard twisted application Boilerplate from twisted.application import service, strports application = service.Application("demoserver") - s = strports.service('tcp:18080', channel.HTTPFactory(site)) + s = strports.service('tcp:18080', factory) s.setServiceParent(application)