class Requests as decorator to decorate SyncApp

This commit is contained in:
dobefore 2022-02-02 18:36:56 +08:00 committed by Vikash Kothary
parent 03bfd123c7
commit 744a9641f5

View File

@ -25,11 +25,11 @@ import sys
import time import time
import unicodedata import unicodedata
import zipfile import zipfile
import types
from webob import Response from webob import Response
from webob.exc import * from webob.exc import *
import urllib.parse import urllib.parse
from functools import wraps
from anki.collection import Collection from anki.collection import Collection
import anki.db import anki.db
import anki.utils import anki.utils
@ -391,20 +391,30 @@ class SyncUserSession:
# for inactivity and then later re-open it (creating a new Collection object). # for inactivity and then later re-open it (creating a new Collection object).
handler.col = col handler.col = col
return handler return handler
class chunked(object): class Requests(object):
'''parse request message from client''' def __init__(self,environ: dict):
# def __init__(self,environ: dict): self.environ=environ
# self.query_string=environ['QUERY_STRING'] @property
# self.environ=environ def params(self):
# self.data=None return self._p
def __init__(self,func=None): @params.setter
self.func = func def params(self,value):
"""
A dictionary-like object containing both the parameters from
the query string and request body.
"""
self._p= value
@property @property
def path(self)-> str: def path(self)-> str:
return self.environ['PATH_INFO'] return self.environ['PATH_INFO']
@property @property
def POST(self): def POST(self):
return self._x
@POST.setter
def POST(self,value):
self._x=value
@property
def parse(self):
'''Return a MultiDict containing all the variables from a form '''Return a MultiDict containing all the variables from a form
request.\n request.\n
include not only post req,but also get''' include not only post req,but also get'''
@ -463,13 +473,13 @@ class chunked(object):
d[k]=''.join(v) d[k]=''.join(v)
return d return d
# # request server with web browser # request server with web browser
# if self.path=='/' : if self.path=='/' :
# d= {'url':b'Anki Sync Server'} d= {'url':b'Anki Sync Server'}
# return d return d
# if self.path=='/favicon.ico' : if self.path=='/favicon.ico' :
# d= {'url':b''} d= {'url':b''}
# return d return d
else: else:
body = env['wsgi.input'].read(length) body = env['wsgi.input'].read(length)
@ -503,34 +513,24 @@ class chunked(object):
v=item[item.rfind(b'"')+1:].decode('utf-8') v=item[item.rfind(b'"')+1:].decode('utf-8')
d[key]=v d[key]=v
return d return d
class chunked(object):
def __call__(self,environ: dict,*args): '''decorator'''
def __init__(self, func):
self.environ=environ wraps(func)(self)
func=self.func def __call__(self, *args, **kwargs):
return func(self,environ) clss=args[0]
@property environ=args[1]
def params(self): start_response = args[2]
""" b=Requests(environ)
A dictionary-like object containing both the parameters from args=(clss,b,)
the query string and request body. w= self.__wrapped__(*args, **kwargs)
""" resp=Response(w)
return resp(environ, start_response)
r=self.POST def __get__(self, instance, cls):
return r if instance is None:
class MultiDict(object): return self
def __init__(self, dicts: dict): else:
if not isinstance(dicts,dict): return types.MethodType(self, instance)
raise TypeError(dicts)
self.dicts=dicts
def __str__(self) -> str:
return dict.__str__(self.dicts)
def __getitem__(self,key):
try:
value = self.dicts[key]
return value
except KeyError:
raise KeyError(key)
class SyncApp: class SyncApp:
valid_urls = SyncCollectionHandler.operations + SyncMediaHandler.operations + ['hostKey', 'upload', 'download'] valid_urls = SyncCollectionHandler.operations + SyncMediaHandler.operations + ['hostKey', 'upload', 'download']
@ -602,17 +602,20 @@ class SyncApp:
# returns user data (not media) as a sqlite3 database for replacing their # returns user data (not media) as a sqlite3 database for replacing their
# local copy in Anki # local copy in Anki
return self.full_sync_manager.download(col, session) return self.full_sync_manager.download(col, session)
@chunked @chunked
def __call__(self, req): def __call__(self, req):
# Get and verify the session # cgi file can only be read once,and will be blocked after being read once more
print(f'hkey {self}') # so i call Requests.parse only once,and bind its return result to properties
# POST and params (set return result as property values)
# can switch back to previous version easily by commenting following two lines
req.params=req.parse
req.POST=req.params
try: try:
hkey = req.params['k'] hkey = req.params['k']
except KeyError: except KeyError:
hkey = None hkey = None
print(f'hkey {self}')
session = self.session_manager.load(hkey, self.create_session) session = self.session_manager.load(hkey, self.create_session)
if session is None: if session is None:
try: try:
skey = req.POST['sk'] skey = req.POST['sk']
@ -626,7 +629,9 @@ class SyncApp:
compression = 0 compression = 0
try: try:
data = req.POST['data'].file.read() # can switch back to previous version easily
# data = req.POST['data'].file.read()
data = req.POST['data']
data = self._decode_data(data, compression) data = self._decode_data(data, compression)
except KeyError: except KeyError:
data = {} data = {}
@ -702,112 +707,6 @@ class SyncApp:
return result return result
return "Anki Sync Server" return "Anki Sync Server"
# def __call__(self, env,start_resp):
# req=Requests(env)
# p=req.params
# # Get and verify the session
# try:
# hkey = p['k']
# except KeyError:
# hkey = None
# session = self.session_manager.load(hkey, self.create_session)
# if session is None:
# try:
# skey = p['sk']
# session = self.session_manager.load_from_skey(skey, self.create_session)
# except KeyError:
# skey = None
# try:
# compression = int(p['c'])
# except KeyError:
# compression = 0
# try:
# data = p['data']
# data = self._decode_data(data, compression)
# except KeyError:
# data = {}
# if req.path.startswith(self.base_url):
# url = req.path[len(self.base_url):]
# if url not in self.valid_urls:
# raise HTTPNotFound()
# if url == 'hostKey':
# result = self.operation_hostKey(data.get("u"), data.get("p"))
# if result:
# resp=Response(json.dumps(result))
# return resp(env,start_resp)
# else:
# # TODO: do I have to pass 'null' for the client to receive None?
# raise HTTPForbidden('null')
# if session is None:
# raise HTTPForbidden()
# if url in SyncCollectionHandler.operations + SyncMediaHandler.operations:
# # 'meta' passes the SYNC_VER but it isn't used in the handler
# if url == 'meta':
# if session.skey == None and 's' in p:
# session.skey = p['s']
# if 'v' in data:
# session.version = data['v']
# if 'cv' in data:
# session.client_version = data['cv']
# self.session_manager.save(hkey, session)
# session = self.session_manager.load(hkey, self.create_session)
# thread = session.get_thread()
# result = self._execute_handler_method_in_thread(url, data, session)
# # If it's a complex data type, we convert it to JSON
# if type(result) not in (str, bytes, Response):
# result = json.dumps(result)
# resp=Response(result)
# return resp(env,start_resp)
# elif url == 'upload':
# thread = session.get_thread()
# result = thread.execute(self.operation_upload, [data['data'], session])
# resp=Response(result)
# return resp(env,start_resp)
# elif url == 'download':
# thread = session.get_thread()
# result = thread.execute(self.operation_download, [session])
# resp=Response(result)
# return resp(env,start_resp)
# # This was one of our operations but it didn't get handled... Oops!
# raise HTTPInternalServerError()
# # media sync
# elif req.path.startswith(self.base_media_url):
# if session is None:
# raise HTTPForbidden()
# url = req.path[len(self.base_media_url):]
# if url not in self.valid_urls:
# raise HTTPNotFound()
# if url == "begin":
# data['skey'] = session.skey
# result = self._execute_handler_method_in_thread(url, data, session)
# # If it's a complex data type, we convert it to JSON
# if type(result) not in (str, bytes):
# result = json.dumps(result)
# resp=Response(result)
# return resp(env,start_resp)
# resp=Response(p['url'])
# return resp(env,start_resp)
@staticmethod @staticmethod
def _execute_handler_method_in_thread(method_name, keyword_args, session): def _execute_handler_method_in_thread(method_name, keyword_args, session):