Projet

Général

Profil

0001-kill-phantomjs-process-if-timeout-14606.patch

Josué Kouka, 27 janvier 2017 16:26

Télécharger (4,82 ko)

Voir les différences:

Subject: [PATCH] kill phantomjs process if timeout (#14606)

 mandayejs/mandaye/utils.py | 61 +++++++++++++++++++++++++++++-----------------
 mandayejs/settings.py      |  2 ++
 tests/settings.py          |  3 +++
 tests/test_mandayejs.py    | 18 ++++++++++++++
 4 files changed, 62 insertions(+), 22 deletions(-)
mandayejs/mandaye/utils.py
17 17
import json
18 18
import subprocess
19 19
import logging
20
import multiprocessing
20 21

  
21 22
from django.conf import settings
22 23

  
......
28 29

  
29 30

  
30 31
def exec_phantom(data, script='do_login.js'):
31
    phantom = subprocess.Popen([
32
        settings.PHANTOM_JS_BINARY,
33
        '--ignore-ssl-errors=yes', '--ssl-protocol=any',
34
        os.path.join(settings.BASE_DIR, 'mandayejs', script)],
35
        close_fds=True,
36
        stdin=subprocess.PIPE,
37
        stdout=subprocess.PIPE
38
    )
32
    result = None
33
    timeout = getattr(settings, 'PHANTOM_JS_TIMEOUT', 10)
34

  
35
    def run(send_end):
36
        phantom = subprocess.Popen([
37
            settings.PHANTOM_JS_BINARY,
38
            '--ignore-ssl-errors=yes', '--ssl-protocol=any',
39
            os.path.join(settings.BASE_DIR, 'mandayejs', script)],
40
            close_fds=True,
41
            stdin=subprocess.PIPE,
42
            stdout=subprocess.PIPE
43
        )
44
        stdout, stderr = phantom.communicate(json.dumps(data))
45

  
46
        try:
47
            result = json.loads(stdout)
48
        except (ValueError,):
49
            result = {"result": "failure, couldn't decode JSON"}
50
            logger.error(stdout)
51

  
52
        if result.get('stderr'):
53
            logger.warning(result['stderr'])
54

  
55
        send_end.send(result)
56

  
57
    recv_end, send_end = multiprocessing.Pipe(False)
58
    process = multiprocessing.Process(target=run, args=(send_end,))
59
    process.start()
60
    process.join(timeout)
61

  
62
    if process.is_alive():
63
        process.terminate()
64
        # remove user's credentials
65
        context = {k: v for k, v in data.items() if k != 'locators'}
66
        logger.error("Phantomjs process timeout, context : %s" % context)
67
        result = {'result': 'failure'}
68
    else:
69
        result = recv_end.recv()
39 70

  
40
    stdout, stderr = phantom.communicate(json.dumps(data))
41

  
42
    try:
43
        result = json.loads(stdout)
44
    except (ValueError,):
45
        result = {"result": "failure, couldn't decode JSON"}
46
        logger.error(stdout)
47

  
48
        # only kill process if it's still running
49
        if phantom.poll() is None:
50
            phantom.terminate()
51

  
52
    if result.get('stderr'):
53
        logger.warning(result['stderr'])
54 71
    return result
55 72

  
56 73

  
mandayejs/settings.py
164 164

  
165 165
PHANTOM_JS_BINARY = '/usr/bin/phantomjs'
166 166

  
167
# Default timeout before killing Phantomjs process
168
PHANTOM_JS_TIMEOUT = 10
167 169

  
168 170
JSONFIELD_ENCODER_CLASS = 'django.core.serializers.json.DjangoJSONEncoder'
169 171

  
tests/settings.py
47 47
api_settings.user_settings.update({
48 48
    'DEFAULT_AUTHENTICATION_CLASSES' : AUTHENTICATION_CLASSES,
49 49
})
50

  
51

  
52
PHANTOM_JS_TIMEOUT = 0.25
tests/test_mandayejs.py
1 1
# -*- coding: utf-8 -*-
2 2

  
3 3
import json
4
import time
4 5
import mock
5 6
import pytest
6 7

  
......
47 48

  
48 49

  
49 50
class MockedPopen(mock.Mock):
51
    stall_for = False
50 52

  
51 53
    def communicate(self, data):
54
        if self.stall_for:
55
            time.sleep(13)
52 56
        return self.expected_output
53 57

  
54 58

  
......
272 276
    assert result['url'] == 'https://whatever.com/someurl'
273 277

  
274 278

  
279
@mock.patch('mandayejs.mandaye.utils.subprocess.Popen')
280
def test_phantom_js_timeout(mocked_popen, caplog):
281
    mocked_popen.return_value = MockedPopen(expected_output=json.dumps({'whatever': 'whatever'}), stall_for=True)
282
    result = exec_phantom(LOGIN_INFO)
283

  
284
    for record in caplog.records():
285
        assert record.levelname == 'ERROR'
286
        assert 'https://whatever.com' in record.message
287
        assert 'tenants/static/js/auth.checker.js' in record.message
288
        assert 'input[type=submit], button' in record.message
289

  
290
    assert result['result'] == 'failure'
291

  
292

  
275 293
@mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS)
276 294
def test_credentials_json_encoding(user_john):
277 295

  
278
-