From 3ff3c15df11c0217ab1086ed295e2635b5d8049c Mon Sep 17 00:00:00 2001 From: Josue Kouka Date: Fri, 27 Jan 2017 06:33:26 +0100 Subject: [PATCH] kill phantomjs if stalled (#14606) --- mandayejs/mandaye/utils.py | 60 +++++++++++++++++++++++++++++----------------- tests/settings.py | 3 +++ tests/test_mandayejs.py | 18 ++++++++++++++ 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/mandayejs/mandaye/utils.py b/mandayejs/mandaye/utils.py index 249948d..fb27d3d 100644 --- a/mandayejs/mandaye/utils.py +++ b/mandayejs/mandaye/utils.py @@ -17,6 +17,7 @@ import os import json import subprocess import logging +import multiprocessing from django.conf import settings @@ -28,29 +29,44 @@ logger = logging.getLogger(__name__) def exec_phantom(data, script='do_login.js'): - phantom = subprocess.Popen([ - settings.PHANTOM_JS_BINARY, - '--ignore-ssl-errors=yes', '--ssl-protocol=any', - os.path.join(settings.BASE_DIR, 'mandayejs', script)], - close_fds=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE - ) + result = None + waiting_time = getattr(settings, 'PHANTOMJS_WAITING_TIME', 10) + + def run(send_end): + phantom = subprocess.Popen([ + settings.PHANTOM_JS_BINARY, + '--ignore-ssl-errors=yes', '--ssl-protocol=any', + os.path.join(settings.BASE_DIR, 'mandayejs', script)], + close_fds=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE + ) + stdout, stderr = phantom.communicate(json.dumps(data)) + + try: + result = json.loads(stdout) + except (ValueError,): + result = {"result": "failure, couldn't decode JSON"} + logger.error(stdout) + + if result.get('stderr'): + logger.warning(result['stderr']) + + send_end.send(result) + + recv_end, send_end = multiprocessing.Pipe(False) + process = multiprocessing.Process(target=run, args=(send_end,)) + process.start() + process.join(waiting_time) + + if process.is_alive(): + process.terminate() + context = {k: v for k, v in data.items() if k != 'locators'} + logger.error("Phantomjs process timeout, context : %s" % context) + result = {'result': 'failure'} + else: + result = recv_end.recv() - stdout, stderr = phantom.communicate(json.dumps(data)) - - try: - result = json.loads(stdout) - except (ValueError,): - result = {"result": "failure, couldn't decode JSON"} - logger.error(stdout) - - # only kill process if it's still running - if phantom.poll() is None: - phantom.terminate() - - if result.get('stderr'): - logger.warning(result['stderr']) return result diff --git a/tests/settings.py b/tests/settings.py index 69387e8..6a8c215 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -47,3 +47,6 @@ else: api_settings.user_settings.update({ 'DEFAULT_AUTHENTICATION_CLASSES' : AUTHENTICATION_CLASSES, }) + + +PHANTOMJS_WAITING_TIME = 2 diff --git a/tests/test_mandayejs.py b/tests/test_mandayejs.py index 5cb83b7..77e43a6 100644 --- a/tests/test_mandayejs.py +++ b/tests/test_mandayejs.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import json +import time import mock import pytest @@ -47,8 +48,11 @@ MOCKED_SITE_LOCATORS = [ class MockedPopen(mock.Mock): + stall_for = False def communicate(self, data): + if self.stall_for: + time.sleep(13) return self.expected_output @@ -272,6 +276,20 @@ def test_phantom_js_errors(mocked_popen, caplog): assert result['url'] == 'https://whatever.com/someurl' +@mock.patch('mandayejs.mandaye.utils.subprocess.Popen') +def test_phantom_js_stalled_process(mocked_popen, caplog): + mocked_popen.return_value = MockedPopen(expected_output=json.dumps({'whatever': 'whatever'}), stall_for=True) + result = exec_phantom(LOGIN_INFO) + + for record in caplog.records(): + assert record.levelname == 'ERROR' + assert 'https://whatever.com' in record.message + assert 'tenants/static/js/auth.checker.js' in record.message + assert 'input[type=submit], button' in record.message + + assert result['result'] == 'failure' + + @mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS) def test_credentials_json_encoding(user_john): -- 2.11.0