1
0
mirror of https://github.com/moparisthebest/SickRage synced 2024-11-14 21:35:03 -05:00

Improved async threading code for WebUI

This commit is contained in:
echel0n 2014-12-08 07:34:14 -08:00
parent d2b6145f8c
commit 46bd600da0
2 changed files with 65 additions and 39 deletions

View File

@ -17,8 +17,8 @@
# along with SickRage. If not, see <http://www.gnu.org/licenses/>. # along with SickRage. If not, see <http://www.gnu.org/licenses/>.
from __future__ import with_statement from __future__ import with_statement
import threading
import threading
import traceback import traceback
import os import os
import time import time
@ -75,11 +75,31 @@ except ImportError:
from Cheetah.Template import Template from Cheetah.Template import Template
from functools import wraps
from tornado.routes import route from tornado.routes import route
from tornado.web import RequestHandler, authenticated, asynchronous from tornado.web import RequestHandler, authenticated, asynchronous
from bug_tracker import BugTracker from bug_tracker import BugTracker
route_locks = {}
def run_async(func):
@wraps(func)
def async_func(*args, **kwargs):
func_hl = threading.Thread(target = func, args = args, kwargs = kwargs)
func_hl.start()
return async_func
@run_async
def run_handler(route, kwargs, callback = None):
try:
res = route(**kwargs)
callback(res, route)
except:
logger.log('Failed doing api request "%s": %s' % (route, traceback.format_exc()), logger.ERROR)
callback({'success': False, 'error': 'Failed returning results'}, route)
def page_not_found(rh): def page_not_found(rh):
index_url = sickbeard.WEB_ROOT index_url = sickbeard.WEB_ROOT
url = rh.request.uri[len(index_url):] url = rh.request.uri[len(index_url):]
@ -91,23 +111,6 @@ def page_not_found(rh):
rh.set_status(404) rh.set_status(404)
rh.write('Wrong API key used') rh.write('Wrong API key used')
class Worker(threading.Thread):
def __init__(self, func, params=None, callback=None, *args, **kwargs):
super(Worker, self).__init__(*args, **kwargs)
self.callback = callback
self.func = func
self.params = params
def run(self):
# Get response
resp = self.func(**self.params)
if resp:
# Issue callback
try:
self.callback(ek.ss(resp).encode('utf-8', 'xmlcharrefreplace'))
except:
self.callback(resp)
class PageTemplate(Template): class PageTemplate(Template):
def __init__(self, rh, *args, **kwargs): def __init__(self, rh, *args, **kwargs):
kwargs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/", kwargs['file']) kwargs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/", kwargs['file'])
@ -260,31 +263,52 @@ class WebHandler(BaseHandler):
route = route.strip('/') route = route.strip('/')
try: try:
route = getattr(self, route, self.index) route = getattr(self, route)
if not callable(route):
self.set_status(404)
self.write('Could not load webui module')
return
except: except:
page_not_found(self) route = getattr(self, 'index')
return
# Sanitize argument lists: # acquire route lock
params = self.request.arguments route_locks[route] = threading.Lock()
for arg, value in params.items(): route_locks[route].acquire()
if len(value) == 1:
params[arg] = value[0]
Worker(route, params, self.worker_done).start()
def worker_done(self, value):
try: try:
self.write(value)
# Sanitize argument lists:
kwargs = self.request.arguments
for arg, value in kwargs.items():
if len(value) == 1:
kwargs[arg] = value[0]
run_handler(route, kwargs, callback=self.taskFinished)
except:
route_locks[route].release()
page_not_found(self)
def taskFinished(self, result, route):
try:
if result:
# encode result data
result = ek.ss(result).encode('utf-8', 'xmlcharrefreplace')
# Check JSONP callback
jsonp_callback = self.get_argument('callback_func', default=None)
if jsonp_callback:
self.write(str(jsonp_callback) + '(' + json.dumps(result) + ')')
self.set_header("Content-Type", "text/javascript")
self.finish()
else:
self.write(result)
self.finish()
except UnicodeDecodeError:
logger.log('Failed proper encode: %s' % traceback.format_exc(), logger.ERROR)
except: except:
logger.log("Failed doing web request '%s': %s" % (route, traceback.format_exc()), logger.ERROR) logger.log("Failed doing web request '%s': %s" % (route, traceback.format_exc()), logger.ERROR)
self.write({'success': False, 'error': 'Failed returning results'}) try:self.finish({'success': False, 'error': 'Failed returning results'})
except:pass
self.finish() # release route lock
route_locks[route].release()
# link post to get # link post to get
post = get post = get

View File

@ -12,6 +12,7 @@ from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.routes import route from tornado.routes import route
class MultiStaticFileHandler(StaticFileHandler): class MultiStaticFileHandler(StaticFileHandler):
def initialize(self, paths, default_filename=None): def initialize(self, paths, default_filename=None):
self.paths = paths self.paths = paths
@ -76,7 +77,7 @@ class SRWebServer(threading.Thread):
if self.enable_https: if self.enable_https:
# If either the HTTPS certificate or key do not exist, make some self-signed ones. # If either the HTTPS certificate or key do not exist, make some self-signed ones.
if not (self.https_cert and os.path.exists(self.https_cert)) or not ( if not (self.https_cert and os.path.exists(self.https_cert)) or not (
self.https_key and os.path.exists(self.https_key)): self.https_key and os.path.exists(self.https_key)):
if not create_https_certificates(self.https_cert, self.https_key): if not create_https_certificates(self.https_cert, self.https_key):
logger.log(u"Unable to create CERT/KEY files, disabling HTTPS") logger.log(u"Unable to create CERT/KEY files, disabling HTTPS")
sickbeard.ENABLE_HTTPS = False sickbeard.ENABLE_HTTPS = False
@ -101,9 +102,10 @@ class SRWebServer(threading.Thread):
self.app.add_handlers(".*$", [ self.app.add_handlers(".*$", [
(r'%s(/?)' % self.options['api_root'], ApiHandler), (r'%s(/?)' % self.options['api_root'], ApiHandler),
(r'%s/getkey(/?)' % self.options['web_root'], KeyHandler), (r'%s/getkey(/?)' % self.options['web_root'], KeyHandler),
(r'%s/api/builder' % self.options['web_root'], RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}), (r'%s/api/builder' % self.options['web_root'], RedirectHandler,
{"url": self.options['web_root'] + '/apibuilder/'}),
(r'%s/login(/?)' % self.options['web_root'], LoginHandler), (r'%s/login(/?)' % self.options['web_root'], LoginHandler),
(r'%s/logout(/?)' % self.options['web_root'], LogoutHandler) (r'%s/logout(/?)' % self.options['web_root'], LogoutHandler),
] + route.get_routes()) ] + route.get_routes())
# Static Path Handlers # Static Path Handlers