osu-wayback/web/asyncTornado.py
2019-03-11 14:41:55 +01:00

128 lines
3.5 KiB
Python

import tornado
import tornado.web
import tornado.gen
from tornado.ioloop import IOLoop
from constants import argumentTypes
from objects import glob
# Copied from https://zxq.co/ripple/ripple-python-common/src/branch/master/web/requestsManager.py
class asyncRequestHandler(tornado.web.RequestHandler):
"""
Tornado asynchronous request handler
create a class that extends this one (requestHelper.asyncRequestHandler)
use asyncGet() and asyncPost() instead of get() and post().
Done. I'm not kidding.
"""
@tornado.web.asynchronous
@tornado.gen.engine
def get(self, *args, **kwargs):
try:
yield tornado.gen.Task(runBackground, (self.asyncGet, tuple(args), dict(kwargs)))
finally:
if not self._finished:
self.finish()
@tornado.web.asynchronous
@tornado.gen.engine
def post(self, *args, **kwargs):
try:
yield tornado.gen.Task(runBackground, (self.asyncPost, tuple(args), dict(kwargs)))
finally:
if not self._finished:
self.finish()
def asyncGet(self, *args, **kwargs):
self.send_error(405)
def asyncPost(self, *args, **kwargs):
self.send_error(405)
def getRequestIP(self):
"""
Return CF-Connecting-IP (request IP when under cloudflare, you have to configure nginx to enable that)
If that fails, return X-Forwarded-For (request IP when not under Cloudflare)
if everything else fails, return remote IP
:return: Client IP address
"""
if "CF-Connecting-IP" in self.request.headers:
return self.request.headers.get("CF-Connecting-IP")
elif "X-Forwarded-For" in self.request.headers:
return self.request.headers.get("X-Forwarded-For")
else:
return self.request.remote_ip
def runBackground(data, callback):
"""
Run a function in the background.
Used to handle multiple requests at the same time
:param data: (func, args, kwargs)
:param callback: function to call when `func` (data[0]) returns
:return:
"""
func, args, kwargs = data
def _callback(result):
IOLoop.instance().add_callback(lambda: callback(result))
glob.pool.apply_async(func, args, kwargs, _callback)
def check_arguments(tornado_inst):
arguments = tornado_inst.request.arguments
arguments_filter = tornado_inst.ARGS
arguments_validator = tornado_inst.ARGS_VALIDATION
args = None
for k, v in arguments_filter.items():
if v == argumentTypes.optional:
args = arg_filter_and(arguments, k)
elif v == argumentTypes.required:
args = arg_filter_require_all(arguments, k)
elif v == argumentTypes.one_required:
args = arg_filter_first(arguments, k, False)
elif v == argumentTypes.only_one:
args = arg_filter_only_one(arguments, k)
if args:
for k, v in arguments_validator.items():
for i in range( len(v) ):
if v[i] in args:
if not k( tornado_inst.get_argument(v[i]) ):
raise Exception("%s is not type of %s" % (v[i], k.__name__ ))
#for arg in args:
# if tornado_inst.get_argument(arg) is not
return args
def arg_filter_and(arguments, filter, can_false = False):
arg_filter = []
for i in filter:
if i in arguments:
arg_filter.append(i)
if can_false:
return arg_filter if len(arg_filter) else False
return arg_filter
def arg_filter_require_all(arguments, required):
for i in required:
if i not in arguments:
return False
return required
def arg_filter_only_one(arguments, required):
arg_filter = []
for i in required:
if i in arguments:
arg_filter.append(i)
return True if len(arg_filter) == 1 else False
def arg_filter_first(arguments, filter, optional = True):
arg_filter = []
for i in filter:
if i in arguments:
arg_filter.append(i)
return arg_filter if len(arg_filter) else optional