Improve naming and comments in FileEtagManager
This commit is contained in:
		
							parent
							
								
									bb82c1e4e7
								
							
						
					
					
						commit
						8ab248a34e
					
				
					 2 changed files with 56 additions and 25 deletions
				
			
		|  | @ -2,9 +2,10 @@ from voussoirkit import cacheclass | |||
| 
 | ||||
| import etiquette | ||||
| 
 | ||||
| class FileCacheManager: | ||||
| class FileEtagManager: | ||||
|     ''' | ||||
|     The FileCacheManager serves ETag and Cache-Control headers for disk files. | ||||
|     The FileEtagManager serves ETag and Cache-Control headers for disk files to | ||||
|     enable client-side caching. | ||||
| 
 | ||||
|     We consider the following cases: | ||||
| 
 | ||||
|  | @ -22,11 +23,50 @@ class FileCacheManager: | |||
|     file's mtime has changed since the last request. | ||||
|     ''' | ||||
|     def __init__(self, maxlen, max_age, max_filesize): | ||||
|         ''' | ||||
|         max_len: | ||||
|             The number of files to track in this cache. | ||||
|         max_age: | ||||
|             Integer number of seconds that will be send to the client as | ||||
|             Cache-Control:max-age=x | ||||
|         max_filesize: | ||||
|             Integer number of bytes. Because we use the file's MD5 as its etag, | ||||
|             you may wish to prevent the reading of large files. Files larger | ||||
|             than this size will not be etagged. | ||||
|         ''' | ||||
|         self.cache = cacheclass.Cache(maxlen=maxlen) | ||||
|         self.max_age = int(max_age) | ||||
|         self.max_filesize = max(int(max_filesize), 0) or None | ||||
| 
 | ||||
|     def get(self, filepath): | ||||
|     def get_304_headers(self, request, filepath): | ||||
|         ''' | ||||
|         Given a request object and a filepath that we would like to send back | ||||
|         as the response, check if the client's provided etag matches the | ||||
|         server's cached etag, and return the headers to be used in a 304 | ||||
|         response (etag, cache-control). | ||||
| 
 | ||||
|         If the client did not provide an etag, or their etag does not match the | ||||
|         current file, or the file cannot be cached, return None. | ||||
|         ''' | ||||
|         client_etag = request.headers.get('If-None-Match', None) | ||||
|         if client_etag is None: | ||||
|             return None | ||||
| 
 | ||||
|         server_value = self.get_file(filepath) | ||||
|         if server_value is None: | ||||
|             return None | ||||
| 
 | ||||
|         server_etag = server_value.get_etag() | ||||
|         if client_etag != server_etag: | ||||
|             return None | ||||
| 
 | ||||
|         return server_value.get_headers() | ||||
| 
 | ||||
|     def get_file(self, filepath): | ||||
|         ''' | ||||
|         Return a FileEtag object if the filepath can be cached, or None if it | ||||
|         cannot (size greater than max_filesize). | ||||
|         ''' | ||||
|         try: | ||||
|             return self.cache[filepath] | ||||
|         except KeyError: | ||||
|  | @ -35,26 +75,15 @@ class FileCacheManager: | |||
|         if (self.max_filesize is not None) and (filepath.size > self.max_filesize): | ||||
|             return None | ||||
| 
 | ||||
|         cache_file = CacheFile(filepath, max_age=self.max_age) | ||||
|         cache_file = FileEtag(filepath, max_age=self.max_age) | ||||
|         self.cache[filepath] = cache_file | ||||
|         return cache_file | ||||
| 
 | ||||
|     def matches(self, request, filepath): | ||||
|         client_etag = request.headers.get('If-None-Match', None) | ||||
|         if client_etag is None: | ||||
|             return False | ||||
| 
 | ||||
|         server_value = self.get(filepath) | ||||
|         if server_value is None: | ||||
|             return False | ||||
| 
 | ||||
|         server_etag = server_value.get_etag() | ||||
|         if client_etag != server_etag: | ||||
|             return False | ||||
| 
 | ||||
|         return server_value.get_headers() | ||||
| 
 | ||||
| class CacheFile: | ||||
| class FileEtag: | ||||
|     ''' | ||||
|     This class represents an individual disk file that is being managed by the | ||||
|     FileEtagManager. | ||||
|     ''' | ||||
|     def __init__(self, filepath, max_age): | ||||
|         self.filepath = filepath | ||||
|         self.max_age = int(max_age) | ||||
|  | @ -68,6 +97,7 @@ class CacheFile: | |||
|         if do_refresh: | ||||
|             self._stored_hash_time = mtime | ||||
|             self._stored_hash_value = etiquette.helpers.hash_file_md5(self.filepath) | ||||
| 
 | ||||
|         return self._stored_hash_value | ||||
| 
 | ||||
|     def get_headers(self): | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ site.debug = True | |||
| site.localhost_only = False | ||||
| 
 | ||||
| session_manager = sessions.SessionManager(maxlen=10000) | ||||
| file_cache_manager = caching.FileCacheManager( | ||||
| file_etag_manager = caching.FileEtagManager( | ||||
|     maxlen=10000, | ||||
|     max_filesize=5 * bytestring.MIBIBYTE, | ||||
|     max_age=BROWSER_CACHE_DURATION, | ||||
|  | @ -240,7 +240,7 @@ def send_file(filepath, override_mimetype=None): | |||
| 
 | ||||
|     file_size = filepath.size | ||||
| 
 | ||||
|     headers = file_cache_manager.matches(request=request, filepath=filepath) | ||||
|     headers = file_etag_manager.get_304_headers(request=request, filepath=filepath) | ||||
|     if headers: | ||||
|         response = flask.Response(status=304, headers=headers) | ||||
|         return response | ||||
|  | @ -292,9 +292,10 @@ def send_file(filepath, override_mimetype=None): | |||
| 
 | ||||
|     outgoing_headers['Accept-Ranges'] = 'bytes' | ||||
|     outgoing_headers['Content-Length'] = (range_max - range_min) + 1 | ||||
|     cache_file = file_cache_manager.get(filepath) | ||||
|     if cache_file is not None: | ||||
|         outgoing_headers.update(cache_file.get_headers()) | ||||
| 
 | ||||
|     file_etag = file_etag_manager.get_file(filepath) | ||||
|     if file_etag is not None: | ||||
|         outgoing_headers.update(file_etag.get_headers()) | ||||
| 
 | ||||
|     if request.method == 'HEAD': | ||||
|         outgoing_data = bytes() | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue