Performance upgrade, lock fix.

This commit is contained in:
retoor 2025-04-08 21:32:18 +02:00
parent d23ed3711a
commit 13f1d2f390

View File

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