From 13f1d2f390afdfc912d24bb63930c9ca47e05f94 Mon Sep 17 00:00:00 2001 From: retoor <retoor@molodetz.nl> Date: Tue, 8 Apr 2025 21:32:18 +0200 Subject: [PATCH] Performance upgrade, lock fix. --- src/snek/webdav.py | 75 ++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/src/snek/webdav.py b/src/snek/webdav.py index 26dce2f..f982e77 100755 --- a/src/snek/webdav.py +++ b/src/snek/webdav.py @@ -1,8 +1,8 @@ import logging import pathlib - +import asyncio logging.basicConfig(level=logging.DEBUG) - +from app.cache import time_cache,time_cache_async import base64 import datetime import mimetypes @@ -18,8 +18,13 @@ from lxml import etree @aiohttp.web.middleware async def debug_middleware(request, handler): print(request.method, request.path, request.headers) - return await handler(request) - + result = await handler(request) + print(result.status) + try: + print(await result.text()) + except: + pass + return result class WebdavApplication(aiohttp.web.Application): def __init__(self, parent, *args, **kwargs): @@ -29,7 +34,6 @@ class WebdavApplication(aiohttp.web.Application): self.locks = {} self.relative_url = "/webdav" - print(self.router) self.router.add_route("OPTIONS", "/{filename:.*}", self.handle_options) self.router.add_route("GET", "/{filename:.*}", self.handle_get) @@ -53,15 +57,6 @@ class WebdavApplication(aiohttp.web.Application): return self.parent.services async def authenticate(self, request): - # session = request.session - # if session.get('uid'): - # request['user'] = await self.services.user.get(uid=session['uid']) - #try: - # request['home'] = await self.services.user.get_home_folder(user_uid=request['user']['uid']) - #except: - # pass - #return request['user'] - auth_header = request.headers.get("Authorization", "") if not auth_header.startswith("Basic "): return False @@ -75,9 +70,7 @@ class WebdavApplication(aiohttp.web.Application): request["home"] = await self.services.user.get_home_folder( request["user"]["uid"] ) - except Exception as ex: - print("GRRRRRRRRRR") - print(ex) + except Exception: pass return request["user"] @@ -109,7 +102,6 @@ class WebdavApplication(aiohttp.web.Application): status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'} ) file_path = request["home"] / request.match_info["filename"] - print("WRITETO_", file_path) file_path.parent.mkdir(parents=True, exist_ok=True) async with aiofiles.open(file_path, "wb") as f: while chunk := await request.content.read(1024): @@ -177,7 +169,6 @@ class WebdavApplication(aiohttp.web.Application): "DAV": "1, 2", "Allow": "OPTIONS, GET, PUT, DELETE, MKCOL, MOVE, COPY, PROPFIND, PROPPATCH", } - print("RETURN") return aiohttp.web.Response(status=200, headers=headers) def get_current_utc_time(self, filepath): @@ -189,25 +180,33 @@ class WebdavApplication(aiohttp.web.Application): "%a, %d %b %Y %H:%M:%S GMT" ) - def get_directory_size(self, directory): + @time_cache_async(10) + async def get_file_size(self, path): + loop = self.parent.loop + stat = await loop.run_in_executor(None,os.stat, path) + return stat.st_size + + @time_cache_async(10) + async def get_directory_size(self, directory): total_size = 0 for dirpath, _, filenames in os.walk(directory): for f in filenames: fp = pathlib.Path(dirpath) / f if fp.exists(): - total_size += fp.stat().st_size + total_size += await self.get_file_size(str(fp)) return total_size - def get_disk_free_space(self, path): - statvfs = os.statvfs(path) + + @time_cache_async(30) + async def get_disk_free_space(self, path="/"): + loop = self.parent.loop + statvfs = await loop.run_in_executor(None,os.statvfs, path) return statvfs.f_bavail * statvfs.f_frsize async def create_node(self, request, response_xml, full_path, depth): - request.match_info.get("filename", "") abs_path = pathlib.Path(full_path) relative_path = str(full_path.relative_to(request["home"])) - href_path = f"{self.relative_url}/{relative_path}".strip(".") href_path = href_path.replace("./","/") href_path = href_path.replace("//", "/") @@ -223,12 +222,12 @@ class WebdavApplication(aiohttp.web.Application): creation_date, last_modified = self.get_current_utc_time(full_path) etree.SubElement(prop, "{DAV:}creationdate").text = creation_date etree.SubElement(prop, "{DAV:}quota-used-bytes").text = str( - full_path.stat().st_size + await self.get_file_size(full_path) if full_path.is_file() - else self.get_directory_size(full_path) + else await self.get_directory_size(full_path) ) etree.SubElement(prop, "{DAV:}quota-available-bytes").text = str( - self.get_disk_free_space(request["home"]) + await self.get_disk_free_space(request["home"]) ) etree.SubElement(prop, "{DAV:}getlastmodified").text = last_modified etree.SubElement(prop, "{DAV:}displayname").text = full_path.name @@ -237,9 +236,9 @@ class WebdavApplication(aiohttp.web.Application): if full_path.is_file(): etree.SubElement(prop, "{DAV:}contenttype").text = mimetype etree.SubElement(prop, "{DAV:}getcontentlength").text = str( - full_path.stat().st_size + await self.get_file_size(full_path) if full_path.is_file() - else self.get_directory_size(full_path) + else await self.get_directory_size(full_path) ) supported_lock = etree.SubElement(prop, "{DAV:}supportedlock") lock_entry_1 = etree.SubElement(supported_lock, "{DAV:}lockentry") @@ -254,7 +253,7 @@ class WebdavApplication(aiohttp.web.Application): etree.SubElement(lock_type_2, "{DAV:}write") etree.SubElement(propstat, "{DAV:}status").text = "HTTP/1.1 200 OK" - if abs_path.is_dir(): + if abs_path.is_dir() and depth > 0: for item in abs_path.iterdir(): await self.create_node(request, response_xml, item, depth - 1) @@ -269,8 +268,6 @@ class WebdavApplication(aiohttp.web.Application): depth = int(request.headers.get("Depth", "0")) except ValueError: pass - - print(request) requested_path = request.match_info.get("filename", "") @@ -285,7 +282,6 @@ class WebdavApplication(aiohttp.web.Application): xml_output = etree.tostring( response_xml, encoding="utf-8", xml_declaration=True ).decode() - print(xml_output) return aiohttp.web.Response( status=207, text=xml_output, content_type="application/xml" ) @@ -302,10 +298,10 @@ class WebdavApplication(aiohttp.web.Application): return aiohttp.web.Response( status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'} ) - request.match_info.get("filename", "/") + resource = request.match_info.get("filename", "/") lock_id = str(uuid.uuid4()) - # self.locks[resource] = lock_id - xml_response = self.generate_lock_response(lock_id) + self.locks[resource] = lock_id + xml_response = await self.generate_lock_response(lock_id) headers = { "Lock-Token": f"opaquelocktoken:{lock_id}", "Content-Type": "application/xml", @@ -320,13 +316,13 @@ class WebdavApplication(aiohttp.web.Application): resource = request.match_info.get("filename", "/") lock_token = request.headers.get("Lock-Token", "").replace( "opaquelocktoken:", "" - ) + )[1:-1] if self.locks.get(resource) == lock_token: del self.locks[resource] return aiohttp.web.Response(status=204) return aiohttp.web.Response(status=400, text="Invalid Lock Token") - def generate_lock_response(self, lock_id): + async def generate_lock_response(self, lock_id): nsmap = {"D": "DAV:"} root = etree.Element("{DAV:}prop", nsmap=nsmap) lock_discovery = etree.SubElement(root, "{DAV:}lockdiscovery") @@ -357,7 +353,6 @@ class WebdavApplication(aiohttp.web.Application): ) requested_path = request.match_info.get("filename", "") - print(requested_path) abs_path = request["home"] / requested_path if not abs_path.exists():