class Requests as decorator to decorate SyncApp
This commit is contained in:
parent
03bfd123c7
commit
744a9641f5
@ -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):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user