From 6ac21218b6fefed4329f5fc82efbccd8082a5d36 Mon Sep 17 00:00:00 2001 From: Josue Kouka Date: Wed, 8 Feb 2017 06:49:02 +0100 Subject: [PATCH] improve generic timeout handling (#14913) --- mandayejs/mandaye/utils.py | 90 ++++++++++++++++++++++++++-------------------- tests/settings.py | 2 +- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/mandayejs/mandaye/utils.py b/mandayejs/mandaye/utils.py index 8050af9..f3594ee 100644 --- a/mandayejs/mandaye/utils.py +++ b/mandayejs/mandaye/utils.py @@ -16,9 +16,9 @@ import os import re import json +import signal import subprocess import logging -import multiprocessing from django.conf import settings @@ -29,49 +29,61 @@ from mandayejs.applications import get_app_settings logger = logging.getLogger(__name__) +class Timeout(object): + + class TimeoutError(Exception): + pass + + def __init__(self, timeout): + self.timeout = timeout + + def __enter__(self): + signal.signal(signal.SIGALRM, self.raise_timeout) + signal.alarm(self.timeout) + + def __exit__(self, *args): + signal.alarm(0) + + def raise_timeout(self, *args): + raise Timeout.TimeoutError + + def exec_phantom(data, script='do_login.js'): - 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: - output = re.search('(.*?)', stdout, re.DOTALL) - if not output: - raise ValueError - stdout = output.group(1) - result = json.loads(stdout) - except (ValueError,): - result = {"result": "json_error"} - logger.error("invalid json: %s" % stdout) - - if result.get('stderr'): - logger.warning(result['stderr']) - if result.get('error'): - logger.error('Error occured: %s' % result.get('reason')) - - send_end.send(result) - - recv_end, send_end = multiprocessing.Pipe(False) - process = multiprocessing.Process(target=run, args=(send_end,)) - process.start() - process.join(settings.PHANTOM_JS_TIMEOUT) - - if process.is_alive(): - process.terminate() + 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 + ) + + # terminate subprocess if timeout + try: + with Timeout(settings.PHANTOM_JS_TIMEOUT): + stdout, stderr = phantom.communicate(json.dumps(data)) + except (Timeout.TimeoutError,): # Don't log locators, they may contain credentials (passwords) + phantom.terminate() context = {k: v for k, v in data.items() if k != 'locators'} logger.error("PhantomJS process timeout, context: %s" % context) - result = {'result': 'timeout'} - else: - result = recv_end.recv() + return {'result': 'timeout'} + + try: + output = re.search('(.*?)', stdout, re.DOTALL) + if not output: + raise ValueError + stdout = output.group(1) + result = json.loads(stdout) + except (ValueError,): + result = {"result": "json_error"} + logger.error("invalid json: %s" % stdout) + + if result.get('stderr'): + logger.warning(result['stderr']) + if result.get('error'): + logger.error('Error occured: %s' % result.get('reason')) return result diff --git a/tests/settings.py b/tests/settings.py index 94e007d..5231f1b 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -49,4 +49,4 @@ api_settings.user_settings.update({ }) -PHANTOM_JS_TIMEOUT = 0.25 +PHANTOM_JS_TIMEOUT = 1 -- 2.11.0