Source code for sirepo.tornado
# -*- coding: utf-8 -*-
"""Wrappers for Tornado
:copyright: Copyright (c) 2020 RadiaSoft LLC. All Rights Reserved.
:license: http://www.apache.org/licenses/LICENSE-2.0.html
"""
from pykern.pkdebug import pkdlog, pkdexc
import sirepo.const
import sirepo.http_util
import tornado.locks
import tornado.queues
[docs]
class Event(tornado.locks.Event):
"""Event with ordered waiters.
When the event is set the waiters are awoken in a FIFO order.
"""
class _OrderedWaiters(list):
def add(self, val):
self.append(val)
def __init__(self):
super().__init__()
self._waiters = self._OrderedWaiters()
[docs]
class AuthHeaderRequestHandler(tornado.web.RequestHandler):
[docs]
@classmethod
def get_header(cls, token):
return sirepo.http_util.auth_header(token)
[docs]
async def get(self, *args, **kwargs):
await self._sr_get(self.__authenticate())
[docs]
async def post(self, *args, **kwargs):
await self._sr_post(self.__authenticate())
[docs]
async def put(self, *args, **kwargs):
await self._sr_put(self.__authenticate())
def __authenticate(self):
if m := sirepo.http_util.parse_auth_header(self.request.headers):
return self._sr_authenticate(m)
raise error_forbidden()
[docs]
class Queue(tornado.queues.Queue):
[docs]
async def get(self):
"""Implements a cancelable Queue.get
See https://github.com/radiasoft/sirepo/issues/2375
"""
x = None
try:
# this returns a future, which may get a result
# before the await returns, that is, if the task
# is canceled after another task has put something
# on the queue and before this task finishes the await.
x = super().get()
return await x
except sirepo.const.ASYNC_CANCELED_ERROR:
if x:
try:
r = x.result()
except BaseException:
# there are many exceptions that can happen,
# including throwing an exception on the Future.
# However, none need to be cascaded as the task
# has already been canceled.
pass
else:
try:
# got a valid result so put it back.
self.task_done()
self.put_nowait(r)
except BaseException as e:
# at this point, the task is canceled so
# we can't raise another exception, but we
# should log that an error has occurred.
# It's an unlikely situation, but definitely a
# bug in the code.
pkdlog(
"exception={} unable to put back result={} stack={}",
e,
r,
pkdexc(),
)
raise
[docs]
def error_forbidden():
return tornado.web.HTTPError(403)
[docs]
def error_not_found():
return tornado.web.HTTPError(404)