do not process message for closed workers

WsockHandler stores a weak reference to the ssh backend worker. The
worker closes itself if the backend connection closes (e.g. the user
exists the ssh session). That happens in parallel to the websocket
handler processing messages, so it is possible for a message to arrive
when the worker no longer has any strong references, leading to an
exception being thrown.

Handle this case by treating the None worker the same way we do invalid
messages: by simply returning.
pull/306/head
Kyle Larose 2022-11-18 08:38:51 -05:00
parent ee24eb7f65
commit f0e2ddb821
No known key found for this signature in database
GPG Key ID: 92497759CAFD5835
2 changed files with 25 additions and 0 deletions

View File

@ -5,6 +5,7 @@ from tornado.httputil import HTTPServerRequest
from tornado.options import options from tornado.options import options
from tests.utils import read_file, make_tests_data_path from tests.utils import read_file, make_tests_data_path
from webssh import handler from webssh import handler
from webssh import worker
from webssh.handler import ( from webssh.handler import (
MixinHandler, WsockHandler, PrivateKey, InvalidValueError MixinHandler, WsockHandler, PrivateKey, InvalidValueError
) )
@ -277,3 +278,19 @@ class TestWsockHandler(unittest.TestCase):
obj.origin_policy = '*' obj.origin_policy = '*'
origin = 'https://blog.example.org' origin = 'https://blog.example.org'
self.assertTrue(WsockHandler.check_origin(obj, origin)) self.assertTrue(WsockHandler.check_origin(obj, origin))
def test_failed_weak_ref(self):
request = HTTPServerRequest(uri='/')
obj = Mock(spec=WsockHandler, request=request)
obj.src_addr = ("127.0.0.1", 8888)
class FakeWeakRef:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return None
ref = FakeWeakRef()
obj.worker_ref = ref
WsockHandler.on_message(obj, b'{"data": "somestuff"}')
self.assertGreaterEqual(ref.count, 1)

View File

@ -559,6 +559,14 @@ class WsockHandler(MixinHandler, tornado.websocket.WebSocketHandler):
def on_message(self, message): def on_message(self, message):
logging.debug('{!r} from {}:{}'.format(message, *self.src_addr)) logging.debug('{!r} from {}:{}'.format(message, *self.src_addr))
worker = self.worker_ref() worker = self.worker_ref()
if not worker:
# The worker has likely been closed. Do not process.
logging.debug(
"received message to closed worker from {}:{}".format(
*self.src_addr
)
)
return
try: try:
msg = json.loads(message) msg = json.loads(message)
except JSONDecodeError: except JSONDecodeError: