]> git.mxchange.org Git - quix0rs-apt-p2p.git/blobdiff - apt_p2p/HTTPServer.py
Multiple peer downloading is mostly working now.
[quix0rs-apt-p2p.git] / apt_p2p / HTTPServer.py
index d252a6386aef205ed397e83579559eb2a33139db..f3d6de72525281df73660f35adc252f26d630414 100644 (file)
@@ -9,7 +9,7 @@ from twisted.internet import defer
 from twisted.web2 import server, http, resource, channel, stream
 from twisted.web2 import static, http_headers, responsecode
 
-from policies import ThrottlingFactory
+from policies import ThrottlingFactory, ThrottlingProtocol, ProtocolWrapper
 from apt_p2p_Khashmir.bencode import bencode
 
 class FileDownloader(static.File):
@@ -56,7 +56,7 @@ class FileDownloader(static.File):
 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
+    Streams the file in 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
@@ -127,6 +127,31 @@ class FileUploader(static.File):
 
         return response
 
+class UploadThrottlingProtocol(ThrottlingProtocol):
+    """Protocol for throttling uploads.
+    
+    Determines whether or not to throttle the upload based on the type of stream.
+    Uploads use L{FileUploaderStream} or L{twisted.web2.stream.MemorySTream},
+    apt uses L{CacheManager.ProxyFileStream} or L{twisted.web.stream.FileStream}.
+    """
+
+    def __init__(self, factory, wrappedProtocol):
+        ThrottlingProtocol.__init__(self, factory, wrappedProtocol)
+        self.throttle = False
+
+    def write(self, data):
+        if self.throttle:
+            ThrottlingProtocol.write(self, data)
+        else:
+            ProtocolWrapper.write(self, data)
+
+    def registerProducer(self, producer, streaming):
+        ThrottlingProtocol.registerProducer(self, producer, streaming)
+        streamType = getattr(producer, 'stream', None)
+        if isinstance(streamType, FileUploaderStream) or isinstance(streamType, stream.MemoryStream):
+            self.throttle = True
+
+
 class TopLevel(resource.Resource):
     """The HTTP server for all requests, both from peers and apt.
     
@@ -137,13 +162,12 @@ class TopLevel(resource.Resource):
     @type manager: L{apt_p2p.AptP2P}
     @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
-    
+    @ivar factory: the factory to use to serve HTTP requests
     """
     
     addSlash = True
     
-    def __init__(self, directory, db, manager):
+    def __init__(self, directory, db, manager, uploadLimit):
         """Initialize the instance.
         
         @type directory: L{twisted.python.filepath.FilePath}
@@ -156,6 +180,9 @@ class TopLevel(resource.Resource):
         self.directory = directory
         self.db = db
         self.manager = manager
+        self.uploadLimit = None
+        if uploadLimit > 0:
+            self.uploadLimit = int(uploadLimit*1024)
         self.factory = None
 
     def getHTTPFactory(self):
@@ -164,7 +191,8 @@ class TopLevel(resource.Resource):
             self.factory = channel.HTTPFactory(server.Site(self),
                                                **{'maxPipeline': 10, 
                                                   'betweenRequestsTimeOut': 60})
-            self.factory = ThrottlingFactory(self.factory, writeLimit = 30*1024)
+            self.factory = ThrottlingFactory(self.factory, writeLimit = self.uploadLimit)
+            self.factory.protocol = UploadThrottlingProtocol
         return self.factory
 
     def render(self, ctx):
@@ -172,9 +200,7 @@ class TopLevel(resource.Resource):
         return http.Response(
             200,
             {'content-type': http_headers.MimeType('text', 'html')},
-            """<html><body>
-            <h2>Statistics</h2>
-            <p>TODO: eventually some stats will be shown here.</body></html>""")
+            self.manager.getStats())
 
     def locateChild(self, request, segments):
         """Process the incoming request."""
@@ -188,7 +214,8 @@ class TopLevel(resource.Resource):
                 return None, ()
             
             # Find the file in the database
-            hash = unquote_plus(segments[1])
+            # Have to unquote_plus the uri, because the segments are unquoted by twisted
+            hash = unquote_plus(request.uri[3:])
             files = self.db.lookupHash(hash)
             if files:
                 # If it is a file, return it
@@ -200,13 +227,17 @@ class TopLevel(resource.Resource):
                     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)
+                log.msg('Hash could not be found in database: %r' % 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, ()
-            
+        
+        # Block access to index .diff files (for now)
+        if 'Packages.diff' in segments or 'Sources.diff' in segments:
+            return None, ()
+         
         if len(name) > 1:
             # It's a request from apt
             return FileDownloader(self.directory.path, self.manager), segments[0:]
@@ -232,7 +263,7 @@ if __name__ == '__builtin__':
                 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)
+    t = TopLevel(FilePath(os.path.expanduser('~')), DB(), None, 0)
     factory = t.getHTTPFactory()
     
     # Standard twisted application Boilerplate