[apt-proxy-devel] r638 - in trunk: . apt_proxy apt_proxy/test debian

Chris Halls halls at alioth.debian.org
Wed Dec 20 15:53:28 CET 2006


Author: halls
Date: Wed Dec 20 15:53:27 2006
New Revision: 638

Modified:
   trunk/apt_proxy/apt_proxy.py
   trunk/apt_proxy/apt_proxy_conf.py
   trunk/apt_proxy/cache.py
   trunk/apt_proxy/clients.py
   trunk/apt_proxy/fetchers.py
   trunk/apt_proxy/test/test_apt_proxy.py
   trunk/apt_proxy/test/test_requests.py
   trunk/aptest
   trunk/debian/changelog
Log:
* Fix username + password parsing in backend Urls
* Add switch for decompressors until code is stable
* Promote some debug error messages to error level
* Add test for username & passwords in backend urls
* Add new test for backend server hangs (not yet complete)
* Unify some test request code
* aptest: make /var/run/apt-proxy if non existent
* Update changelog


Modified: trunk/apt_proxy/apt_proxy.py
==============================================================================
--- trunk/apt_proxy/apt_proxy.py	(original)
+++ trunk/apt_proxy/apt_proxy.py	Wed Dec 20 15:53:27 2006
@@ -191,7 +191,7 @@
 
         if '@' in netloc:
             auth = netloc[:netloc.rindex('@')]
-            netloc = netloc[netloc.rindex('@'):]
+            netloc = netloc[netloc.rindex('@')+1:]
             self.username, self.password = auth.split(':')
         else:
             self.username = None

Modified: trunk/apt_proxy/apt_proxy_conf.py
==============================================================================
--- trunk/apt_proxy/apt_proxy_conf.py	(original)
+++ trunk/apt_proxy/apt_proxy_conf.py	Wed Dec 20 15:53:27 2006
@@ -100,7 +100,8 @@
         ['dynamic_backends', 'on', 'boolean'],
         ['http_proxy', None , 'proxyspec'],
         ['username', 'aptproxy', 'string'],
-        ['bandwidth_limit', None, '*int']
+        ['bandwidth_limit', None, '*int'],
+        ['use_experimental_decompressors', False, 'boolean']
         ]
 
     """

Modified: trunk/apt_proxy/cache.py
==============================================================================
--- trunk/apt_proxy/cache.py	(original)
+++ trunk/apt_proxy/cache.py	Wed Dec 20 15:53:27 2006
@@ -30,6 +30,18 @@
 
 import clients
 
+class InvalidCachePath(Exception):
+    """
+    Exception generated if client specifies an invalid path
+    """
+    def __init__(self, path, position, reason):
+        self.path = path
+        self.position = position
+        self.reason = reason
+    def __str__(self):
+        return "Invalid cache path (%s) at position %i: %s" % \
+                 (self.path, self.position, self.reason)
+
 class CacheEntry:
     """
     This class manages operations on a file in the cache.  Each physical
@@ -68,7 +80,7 @@
         """
         Create a new cache entry
         @param backend Backend where this entry belongs
-        @param path Path to file within backend directory
+        @param path Path to file within backend directory (not escaped)
         """
         self.backend = backend
         self.factory = backend.factory
@@ -102,10 +114,12 @@
 
         self.fetcher = None
 
-        if self.filetype.decompressor is not None:
-            # File needs to be decompressed
-            self.filetype.decompressor(self)
-            
+        # decompressors are currently disabled until debugged :)
+        if self.factory.config.use_experimental_decompressors:
+            if self.filetype.decompressor is not None:
+                # File needs to be decompressed
+                self.filetype.decompressor(self)
+
     def add_request(self, request):
         """
         A new request has been received for this file

Modified: trunk/apt_proxy/clients.py
==============================================================================
--- trunk/apt_proxy/clients.py	(original)
+++ trunk/apt_proxy/clients.py	Wed Dec 20 15:53:27 2006
@@ -24,7 +24,7 @@
 CacheEntry objects will notify subscribed Clients of changes in state
 """
 
-import os, re, urlparse, gzip, bz2
+import os, re, urlparse, urllib, gzip, bz2
 from StringIO import StringIO
 
 from twisted.web import http
@@ -55,13 +55,18 @@
         """
         self.uri = self.clean_path(self.uri)
 
+        if re.search(self.uri, '[^a-zA-Z0-9~,.+%:;@#?{}[]()$-]'):
+            log.err("Invalid characters found in filename")
+            self.finishCode(http.FORBIDDEN, "Invalid characters in filename")
+            return
+
         if_modified_since = self.getHeader('if-modified-since')
         if if_modified_since != None:
             self.if_modified_since = http.stringToDatetime(
                     if_modified_since)
 
         if self.uri[0] != '/':
-            log.debug("Request must include at least one '/'")
+            log.err("Request must include at least one '/'")
             self.finishCode(http.FORBIDDEN, "Request must include at least one '/'")
             return
 
@@ -71,17 +76,18 @@
 
         if self.method != 'GET':
             #we currently only support GET
-            log.debug("abort - method not implemented", 'HttpRequestClient')
+            log.err("abort - method not implemented", 'HttpRequestClient')
             self.finishCode(http.NOT_IMPLEMENTED)
             return
 
         if re.search('/\.\./', self.uri):
-            log.debug("/../ in simplified uri ("+self.uri+")", 'HttpRequestClient')
+            log.err("/../ in simplified uri ("+self.uri+")", 'HttpRequestClient')
             self.finishCode(http.FORBIDDEN)
             return
 
         self.backend = self.factory.getBackend(backendName)
         if self.backend is None:
+            log.err("backend %s not found" % (backendName), 'HttpRequestClient')
             self.finishCode(http.NOT_FOUND, "NON-EXISTENT BACKEND")
             return None
 
@@ -89,7 +95,7 @@
 
         elements = self.uri.split('/', 2)
         if len(elements) < 3:
-            log.debug("abort - too few slashes in URI %s" % (self.uri), 'Request')
+            log.err("abort - too few slashes in URI %s" % (self.uri), 'Request')
             self.finishCode(http.FORBIDDEN, 'too few slashes in URI %s' % (self.uri))
             return
 
@@ -97,14 +103,14 @@
         self.cacheEntry = self.backend.get_cache_entry(backend_path)
 
         if not self.cacheEntry.filetype:
-            log.debug("abort - unknown extension for file %s" % (backend_path), 'HttpRequestClient')
+            log.err("abort - unknown extension for file %s" % (backend_path), 'HttpRequestClient')
             self.finishCode(http.FORBIDDEN, 'File not found - unknown extension')
             return
 
         self.setHeader('content-type', self.cacheEntry.filetype.contype)
 
         if os.path.isdir(self.cacheEntry.file_path):
-            log.debug("abort - Directory listing not allowed", 'HttpRequestClient')
+            log.err("abort - Directory listing not allowed", 'HttpRequestClient')
             self.finishCode(http.FORBIDDEN, 'Directory listing not permitted')
             return
 
@@ -113,7 +119,8 @@
     def clean_path(self, uri):
         # Clean up URL given
         scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
-        return os.path.normpath(path)
+        unquoted_path = urllib.url2pathname(path)
+        return os.path.normpath(unquoted_path)
 
     def not_modified(self):
         """
@@ -153,7 +160,10 @@
         log.debug("finish. fileno:%s uri:%s" % (self.getFileno(), self.uri) , 'HttpRequestClient')
         #import traceback
         #traceback.print_stack()
-        http.Request.finish(self)
+        try:
+            http.Request.finish(self)
+        except Exception, e:
+            log.debug("Unexpected error finishing http request: %s" % (e), 'HttpRequestClient')
 
         if self.cacheEntry:
             reactor.callLater(0, self.cacheEntry.remove_request, self)

Modified: trunk/apt_proxy/fetchers.py
==============================================================================
--- trunk/apt_proxy/fetchers.py	(original)
+++ trunk/apt_proxy/fetchers.py	Wed Dec 20 15:53:27 2006
@@ -21,7 +21,7 @@
 network backends
 """
 
-import re, os, string, time, glob, signal, stat, base64
+import re, os, string, time, glob, signal, stat, base64, urllib
 from twisted.web import static, http
 from twisted.internet import protocol, reactor, defer, error, abstract
 from twisted.python import failure
@@ -125,7 +125,7 @@
 
     def download_failed(self, reason_code, reason_msg):
         #self.cacheEntry.download_data_end()
-        log.debug("download_failed: (%s) %s " %(reason_code, reason_msg), "Fetcher")
+        log.msg("download_failed: (%s) %s " %(reason_code, reason_msg), "Fetcher")
         if self.fetcher is not None and not self.fetcher.pipelining:
             self.connection_closed(self.fetcher)
         self.cacheEntry.download_failure(reason_code, reason_msg)
@@ -306,6 +306,7 @@
         self.fetcher = fetcher
         self.uri = uri
         self.finished = False
+        self.http_status = None
         backendServer = self.parent.backendServer
         if self.proxy is None:
             serverpath = backendServer.path
@@ -315,8 +316,9 @@
                 serverpath = serverpath + ":" + str(backendServer.port)
             serverpath = serverpath + "/" + backendServer.path 
 
+        get_path = serverpath + "/" + urllib.quote(uri)
         #self.sendCommand(self.request.method, 
-        self.sendCommand("GET", serverpath + "/" + uri)
+        self.sendCommand("GET", get_path)
 
         self.sendHeader('host', backendServer.host)
         if self.proxy is not None and self.proxy.user is not None:
@@ -473,6 +475,9 @@
         else:
             # Limit download rate
             factory = policies.ThrottlingFactory(self, readLimit=self.read_limit)
+        self.timeout = self.backendServer.backend.config.timeout
+        if self.timeout:
+            factory = policies.TimeoutFactory(self, timeoutPeriod=self.timeout)
         reactor.connectTCP(host, port, factory, self.backendServer.backend.config.timeout)
         return self.connectCallback
 
@@ -501,7 +506,10 @@
         %param uri: URI of file to be downloaded within backend
         %param mtime: Modification time of current file in cache
         """
-        self.connection.download(fetcher, uri, mtime)
+        if self.connection:
+            self.connection.download(fetcher, uri, mtime)
+        else:
+            fetcher.fetcher_internal_error("http_client self.connection == None")
 
     def disconnect(self):
         if self.isConnected:
@@ -609,11 +617,15 @@
     def ftpMtimeFailed(self, msgs):
         if msgs.check(ftp.CommandFailed):
             code = msgs.getErrorMessage()[2:5]
-            log.debug("ftp fetch of Mtime failed: %s code:%s" % (msgs.getErrorMessage(), code), 'ftp_client')
+            log.err("ftp fetch of Mtime failed: %s code:%s" % (msgs.getErrorMessage(), code), 'ftp_client')
             if code == '550':
                 # Not found
                 self.parent.file_not_found()
                 return
+        elif msgs.check(ftp.ConnectionLost):
+            log.err("ftp connection lost: %s" % (msgs.getErrorMessage()), 'ftp_client')
+            self.parent.download_failed(http.SERVICE_UNAVAILABLE, "Connection lost to FTP server")
+            return
         log.debug("ftp fetch of Mtime for %s unknown failure: %s" % (self.remote_file, msgs), 'ftp_client')
         self.ftpFetchSize()
 
@@ -654,7 +666,7 @@
         fetcher.ftpFetchFile()
 
     def ftpListFailed(self, msgs):
-        log.debug("ftp list failed: %s" % (msgs), 'ftp_client')
+        log.err("ftp list failed: %s" % (msgs), 'ftp_client')
         self.parent.fetcher_internal_error("Could not list directory")
 
     def ftpFetchFile(self):
@@ -833,7 +845,7 @@
         self.file_path = fetcher.cacheEntry.file_path   # Absolute path of file
         self.cache_dir = fetcher.cacheEntry.filedir
         self.remote_file = (self.backendServer.path + '/' 
-                            + uri)
+                            + urllib.quote(uri))
 
         # Change /path/to/FILE -> /path/to/.FILE.* to match rsync tempfile
         self.globpattern = re.sub(r'/([^/]*)$', r'/.\1.*', self.file_path)
@@ -954,10 +966,18 @@
             log.debug("Status: %s" %(status_object.value.exitCode)
                       ,'rsync_client')
             exitcode = status_object.value.exitCode
-            if exitcode == 10:
-                # Host not found
-                self.parent.connection_failed('rsync connection to %s failed'
-                                                % (self.backendServer.host))
+            class rsyncError(Exception):
+                def __init__(self, host, code):
+                    if code == 10: msg = "Host not found"
+                    elif code == 12: msg = "error in protocol stream"
+                    else: msg = "unhandled error"
+                    self.error = "rsync://%s failed with code %i:%s" % (host, code, msg)
+                def __str__(self):
+                    return "rsyncError: " + self.error
+                
+            if exitcode in (10,12):
+                self.parent.connection_failed(
+                    failure.Failure(rsyncError(self.backendServer.host, exitcode)))
             elif exitcode == 23:
                 self.parent.file_not_found()
             else:
@@ -1101,4 +1121,4 @@
 
     def stop(self):
         for q in self.queues.values():
-            q.stop()
\ No newline at end of file
+            q.stop()

Modified: trunk/apt_proxy/test/test_apt_proxy.py
==============================================================================
--- trunk/apt_proxy/test/test_apt_proxy.py	(original)
+++ trunk/apt_proxy/test/test_apt_proxy.py	Wed Dec 20 15:53:27 2006
@@ -274,7 +274,8 @@
          rsync://server3/path3
          file://server4/path4
 [test_usernames]
-backends=http://myUser:thePassword@server/path
+backends=http://myUser:thePassword@httpserver/httppath
+         ftp://myFtpUser:theFtpPassword@ftpserver/ftppath
 """
 class BackendServerTest(FactoryTestHelper):
     def setUp(self):
@@ -283,17 +284,26 @@
         """
         FactoryTestHelper.setUp(self, backendServerConfig)
         self.backend = self.factory.getBackend('test_servers')
+        self.backend2 = self.factory.getBackend('test_usernames')
 
     def testServerHosts(self):
         values = ['server1','server2','server3','server4']
         for server in self.backend.uris:
             value = values[self.backend.uris.index(server)]
             self.assertEquals(server.host, value)
+        values = ['httpserver','ftpserver']
+        for server in self.backend2.uris:
+            value = values[self.backend2.uris.index(server)]
+            self.assertEquals(server.host, value)
     def testServerPaths(self):
         values = ['/path1','/path2','/path3','/path4']
         for server in self.backend.uris:
             value = values[self.backend.uris.index(server)]
             self.assertEquals(server.path, value)
+        values = ['/httppath','/ftppath']
+        for server in self.backend2.uris:
+            value = values[self.backend2.uris.index(server)]
+            self.assertEquals(server.path, value)
     def testServerProtocols(self):
         values = ['http','ftp','rsync','file']
         for server in self.backend.uris:
@@ -313,11 +323,12 @@
     def testNoPassword(self):
         self.assertEquals(self.backend.uris[0].password,None)
     def testUser(self):
-        backend = self.factory.getBackend('test_usernames')
-        self.assertEquals(backend.uris[0].username,'myUser')
+        self.assertEquals(self.backend2.uris[0].username,'myUser')
+        self.assertEquals(self.backend2.uris[1].username,'myFtpUser')
     def testPassword(self):
         backend = self.factory.getBackend('test_usernames')
-        self.assertEquals(backend.uris[0].password,'thePassword')
+        self.assertEquals(self.backend2.uris[0].password,'thePassword')
+        self.assertEquals(self.backend2.uris[1].password,'theFtpPassword')
 
 class testRequests(unittest.TestCase):
     def setUp(self):

Modified: trunk/apt_proxy/test/test_requests.py
==============================================================================
--- trunk/apt_proxy/test/test_requests.py	(original)
+++ trunk/apt_proxy/test/test_requests.py	Wed Dec 20 15:53:27 2006
@@ -16,7 +16,7 @@
 
 """This module tests the client protocol itself"""
 
-import os, time
+import os, time, urllib
 from twisted.trial import unittest
 from twisted.internet import protocol, reactor, defer
 from twisted.python import failure
@@ -92,9 +92,10 @@
         log.debug("connection made to test apt-proxy server", 'uriRequester')
         for i in range(0,len(self.tests)):
             test = self.tests[i]
-            log.debug("requesting: %s" %(test.filename), 'uriRequester')
+            url = urllib.quote(test.filename)
+            log.debug("requesting: %s" %(url), 'uriRequester')
             #self.sendCommand("GET", test.filename)
-            self.transport.write('%s %s HTTP/1.1\r\n' % ("GET", test.filename))
+            self.transport.write('%s %s HTTP/1.1\r\n' % ("GET", url))
 
             self.sendHeader('Host', self.host)
             if len(self.tests)>1 and i != len(self.tests):
@@ -114,14 +115,16 @@
         self.firstLine = 1 
         #self.length = None
         self.__buffer = ''
+        log.debug("getNextTest")
 
         if len(self.tests):
             self.nextTest = self.tests[0]
             self.tests = self.tests[1:]
             log.debug("waiting for test results for: " + self.nextTest.filename, 'uriRequester')
         else:
-            log.debug('test passed', 'uriRequester')
+            log.debug('final test passed', 'uriRequester')
             self.connection.disconnect()
+            log.debug("============ callback 127 [%s]" % (self.deferred))
             self.deferred.callback(None)
             
     def handleStatus(self, version, code, message):
@@ -144,6 +147,7 @@
         else:
             log.debug("aborting transfer", 'uriRequester')
             self.disconnect()
+            log.debug("============ callback 150")
             self.deferred.callback(None)
             #self.passed() # Trigger disconnection of connection
 
@@ -186,6 +190,7 @@
     def failed(self, data):
         log.debug('test failed', 'uriRequester')
         self.disconnect()
+        log.debug("============ errorback 193")
         self.deferred.errback(data)
     def disconnect(self):
         reactor.callLater(0, self.connection.disconnect)
@@ -279,19 +284,31 @@
     """
     Class to perform a series of requests against a test backend.
     Derived classes should arrange for a local server to serve
-    files from the test_daya directory.
+    files from the test_data directory.
     """
 
     # Name of test backend
     backendName = 'test_data'
     packagesTestFile = '/packages/Packages'
 
-    def setUp(self, backend_uri):
+    def setUp(self, debugName, protocol, serverFactory, uriSuffix=None):
         """
         Make a configuration with a single backend
-
-        @param backend_uri: backend server uri e.g. http://127.0.0.1:1234
-        """
+        
+        @param debugName Name of class to print in debug messages
+        @param protocol Protocol of fetcher to be tested (http, ftp, rysnc etc)
+        @param serverFactory Class to provide start() and stop() methods for backend server
+        @param uriSuffix This is added to uri requests if necessary
+        """
+        self.debugname = debugName
+        self.protocol = protocol
+        self.server = serverFactory()
+        self.uriSuffix = uriSuffix
+        self.port = self.server.start()
+
+        backend_uri = self.protocol + "://127.0.0.1:" + str(self.port)
+        if self.uriSuffix:
+            backend_uri += '/' + self.uriSuffix
         config = ("dynamic_backends=off\n" +
                   "[test_data]\n" +
                   "backends=" + str(backend_uri))
@@ -299,11 +316,10 @@
         self.testfilesdir = os.path.normpath(os.getcwd()+"/../test_data")
     def tearDown(self):
         log.debug("tearDown", self.debugname)
+        reactor.iterate(0.1)
+        self.server.stop()
+        reactor.iterate(0.1)
         TestRequestHelper.tearDown(self)
-    def testNotFound(self):
-        return self.doRequest(uriData('/test_data/NotHere.gz', http.NOT_FOUND))
-    testNotFound.timeout = 2
-
     def downloadFile(self, file=packagesTestFile):
         """
         Download a file to cache
@@ -318,7 +334,10 @@
         Given a filename, generate real filename and request path
         """
         filename = '/' + self.backendName + file
-        sourcepath = self.testfilesdir+file
+        if hasattr(self, 'testfilesdir'):
+            sourcepath = self.testfilesdir+file
+        else:
+            sourcepath = None
         destpath = self.cache_dir + filename
         return filename, sourcepath, destpath
 
@@ -351,6 +370,13 @@
         d.addCallback(checkPath)
         return d
 
+class BackendProtocolTests(BackendTestBase):
+    """
+    Class which implements tests which should be run for all tested protocols
+    """
+    def testNotFound(self):
+        return self.doRequest(uriData('/test_data/NotHere.gz', http.NOT_FOUND))
+    testNotFound.timeout = 2
     def testPackagesFile(self):
         return self.downloadFile().addCallback(self.PackagesFile2)
     def PackagesFile2(self, x):
@@ -368,10 +394,12 @@
         filename = self.calcFilePaths(self.packagesTestFile)[0][1:] # Remove leading '/' from path
         backend = self.factory.getBackend(self.backendName)
         self.assertEquals(backend.get_packages_db().packages.get_files(), [filename])
+    testPackagesGzFile.todo = "Need to test this"
 
     def testPackagesBz2File(self):
         return self.downloadFile(self.packagesTestFile+'.bz2').addCallback(self.PackagesUncompressed)
     testPackagesBz2File.timeout = 2
+    testPackagesBz2File.todo = "Need to test this code"
 
     def testNotModifiedGreater(self):
         "Check http not modified is sent for new file"
@@ -517,6 +545,16 @@
         return self.downloadFiles('/packages/tilde~test.txt')
     testTilde.timeout = 10
 
+    def testConnectionRefused(self):
+        self.server.stop()
+        reactor.iterate(0.1)
+        filename, sourcepath, destpath = self.getFilePaths('/packages/apt_0.0.1_test.deb')
+        d = self.doRequest(uriData(filename, http.SERVICE_UNAVAILABLE))
+        d.addBoth(lambda x: self.server.start())
+        return d
+    testConnectionRefused.timeout = 2
+        
+        
     #def testTimeout(self):
         #pass
     #testTimeout.todo = True
@@ -535,23 +573,18 @@
     # - file mtime is same as server mtime
     # - correct file path is entered in databases after download
 
-class HttpBackendTest(TestRequestHelper, BackendTestBase):
+class HttpBackendTest(TestRequestHelper, BackendProtocolTests):
     def setUp(self):
         """
         Make a configuration with a single backend
         [files]
         backends=file:///<path to test packages directory>
         """
-        self.debugname = 'HttpBackendTest'
-        self.httpserver = WebServer()
-        port = self.httpserver.start()
-        uri = "http://127.0.0.1:" + str(port)
-        BackendTestBase.setUp(self, uri)
+        BackendTestBase.setUp(self, 'HttpBackendTest', 'http', WebServer)
     def tearDown(self):
-        self.httpserver.stop()
         BackendTestBase.tearDown(self)
 
-class FtpBackendTest(TestRequestHelper, BackendTestBase):
+class FtpBackendTest(TestRequestHelper, BackendProtocolTests):
     def setUp(self):
         """
         Make a configuration with a single backend
@@ -559,37 +592,26 @@
         backends=file:///<path to test packages directory>
         """
         import test_fetchers
-        import twisted
-        self.debugname = 'FtpBackendTest'
-        self.ftpserver = test_fetchers.FtpServer()
-        port = self.ftpserver.start()
-        uri = "ftp://127.0.0.1:" + str(port)
-        BackendTestBase.setUp(self, uri)
+        BackendTestBase.setUp(self, 'FtpBackendTest', 'ftp', test_fetchers.FtpServer)
     def tearDown(self):
-        self.ftpserver.stop()
         BackendTestBase.tearDown(self)
 
-class RsyncBackendTest(TestRequestHelper, BackendTestBase):
+class RsyncBackendTest(TestRequestHelper, BackendProtocolTests):
     def setUp(self):
         """
         Make a configuration with a single backend
         [files]
         backends=file:///<path to test packages directory>
         """
-        self.debugname = 'RsyncBackendTest'
-        self.rsyncserver = RsyncServer()
-        port = self.rsyncserver.start()
-        uri = "rsync://127.0.0.1:" + str(port) + '/apt-proxy'
-        BackendTestBase.setUp(self, uri)
+        BackendTestBase.setUp(self, 'RsyncBackendTest', 'rsync', RsyncServer, uriSuffix='apt-proxy')
     def tearDown(self):
-        self.rsyncserver.stop()
-        reactor.iterate(0.1)
         BackendTestBase.tearDown(self)
     def testTempFile(self):
         "rysnc Tempfile is detected"
         b = self.factory.getBackend(self.backendName)
         b.config.bandwidth_limit = 100000
-        self.downloadFile(file='/packages/apt_0.0.1_test.deb')
+        filename, sourcepath, destpath = self.getFilePaths('/packages/apt_0.0.1_test.deb')
+        self.doRequest(uriData(filename, None))
         reactor.callLater(0.5, self.TempFile2)
         self.testResult = defer.Deferred()
         return self.testResult
@@ -604,4 +626,82 @@
             self.testResult.callback("Tempfile is %s" %(file))
         else:
             raise UnknownFailure()
-    testTempFile.timeout=2
\ No newline at end of file
+    testTempFile.timeout=2
+
+
+# HangTest classes
+#
+# Define a protocol that allows a connection but does not respond
+class HangProtocol(protocol.Protocol):
+    pass
+class HangFactory(protocol.ServerFactory):
+    protocol = HangProtocol
+class HangServer:
+    def start(self):
+        self.port = reactor.listenTCP(0, HangFactory(), interface="127.0.0.1")
+        return self.port.getHost().port
+    def stop(self):
+        self.port.stopListening()
+class HangTestBase(BackendTestBase):
+    def setUp(self, protocol):
+        BackendTestBase.setUp(self,protocol + 'HangTest', protocol, HangServer)
+    def tearDown(self):
+        BackendTestBase.tearDown(self)
+    def testConnectionHang(self):
+        b = self.factory.getBackend(self.backendName)
+        b.config.timeout = 1
+        filename, sourcepath, destpath = self.getFilePaths('/packages/apt_0.0.1_test.deb')
+        d = self.doRequest(uriData(filename, http.SERVICE_UNAVAILABLE))
+        def printTrace(x):
+            import traceback
+            log.debug("-------test errback------")
+            print("test errback----")
+            traceback.print_stack()
+        def printOK(x):
+            log.debug("---trace OK ---")
+        d.addCallback(printOK)
+        d.addErrback(printTrace)
+        return d
+    testConnectionHang.timeout = 5
+
+class HttpHangTest(HangTestBase, TestRequestHelper):
+    def setUp(self):
+        HangTestBase.setUp(self, 'http')
+class FtpHangTest(HangTestBase, TestRequestHelper):
+    def setUp(self):
+        HangTestBase.setUp(self, 'ftp')
+class RsyncHangTest(HangTestBase, TestRequestHelper):
+    def setUp(self):
+        HangTestBase.setUp(self, 'rsync')
+
+# Attempt a connection to a server that is not responding
+dead_ip_address = '127.0.0.2:1'
+
+class NoConnectionBase(BackendTestBase):
+    def setUp(self, protocol):
+        self.debugname = protocol + 'HangTest'
+        backend_uri = protocol + "://"+ dead_ip_address
+
+        config = ("dynamic_backends=off\n" +
+                  "[test_data]\n" +
+                  "backends=" + backend_uri)
+        TestRequestHelper.setUp(self, config)
+    def tearDown(self):
+        TestRequestHelper.tearDown(self)
+    def testConnectionTimeout(self):
+        b = self.factory.getBackend(self.backendName)
+        b.config.timeout = 1
+        filename, sourcepath, destpath = self.getFilePaths('/packages/apt_0.0.1_test.deb')
+        d = self.doRequest(uriData(filename, http.SERVICE_UNAVAILABLE))
+        return d
+    testConnectionTimeout.timeout = 2
+
+class HttpNoConnectionTest(NoConnectionBase, TestRequestHelper):
+    def setUp(self):
+        NoConnectionBase.setUp(self, 'http')
+class FtpNoConnectionTest(NoConnectionBase, TestRequestHelper):
+    def setUp(self):
+        NoConnectionBase.setUp(self, 'http')
+class HttpNoConnectionTest(NoConnectionBase, TestRequestHelper):
+    def setUp(self):
+        NoConnectionBase.setUp(self, 'http')

Modified: trunk/aptest
==============================================================================
--- trunk/aptest	(original)
+++ trunk/aptest	Wed Dec 20 15:53:27 2006
@@ -2,6 +2,8 @@
 
 profile=
 
+mkdir -p /var/run/apt-proxy || exit -1
+
 # If you run 'aptest profile' you will get a dump of all functions to ap-profile.log
 if [ "$1" = profile ]; then
   profile="-p ap-profile.log"

Modified: trunk/debian/changelog
==============================================================================
--- trunk/debian/changelog	(original)
+++ trunk/debian/changelog	Wed Dec 20 15:53:27 2006
@@ -4,16 +4,24 @@
   * Change the meaning of min_refresh_delay parameter, so the
     delay is measured from the modification time of the file on the
     backend instead of the time a client last requested this file.
+    Now apt-proxy will always query backends when a file is too
+    old (Closes: #266000)
+  * Set process name to apt-proxy
+  * Properly deal with escaped characters, including ~ in URLs on FTP
+    backends. Unescape URLs and check for invalid characters when parsing
+    a request. A big thanks to Ben Hutchings for the patch 
+    (Closes: #393483, #366262)
+  * Fix username at password given in backend server URLs and add a test case.
+    Thanks Jason Thomas for the patch (Closes: #348985)
   * Uncompress Packages.gz and Packages.bz2 on the fly, and
     update databases from these files (Closes: #TODO)
-  * Set process name to apt-proxy
 
   [ Mark Sheppard ]
   * Generate an error if a client attempts to retrieve
     http://server:9999/  (Closes: #386546)
   * When returning an error, generate an HTML page containing the error
 
- -- Chris Halls <halls at debian.org>  Wed, 27 Sep 2006 18:29:15 +0100
+ -- Chris Halls <chris.halls at credativ.co.uk>  Wed, 13 Dec 2006 22:33:24 +0100
 
 apt-proxy (1.9.35) unstable; urgency=low
 



More information about the apt-proxy-devel mailing list