| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Start and stop Web Page Replay. | 6 """Start and stop Web Page Replay. |
| 7 | 7 |
| 8 Of the public module names, the following one is key: | 8 Of the public module names, the following one is key: |
| 9 ReplayServer: a class to start/stop Web Page Replay. | 9 ReplayServer: a class to start/stop Web Page Replay. |
| 10 """ | 10 """ |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 | 28 |
| 29 # Chrome options to make it work with Web Page Replay. | 29 # Chrome options to make it work with Web Page Replay. |
| 30 def GetChromeFlags(replay_host, http_port, https_port): | 30 def GetChromeFlags(replay_host, http_port, https_port): |
| 31 return [ | 31 return [ |
| 32 '--host-resolver-rules=MAP * %s,EXCLUDE localhost' % replay_host, | 32 '--host-resolver-rules=MAP * %s,EXCLUDE localhost' % replay_host, |
| 33 '--testing-fixed-http-port=%s' % http_port, | 33 '--testing-fixed-http-port=%s' % http_port, |
| 34 '--testing-fixed-https-port=%s' % https_port, | 34 '--testing-fixed-https-port=%s' % https_port, |
| 35 '--ignore-certificate-errors', | 35 '--ignore-certificate-errors', |
| 36 ] | 36 ] |
| 37 | 37 |
| 38 |
| 38 # Signal masks on Linux are inherited from parent processes. If anything | 39 # Signal masks on Linux are inherited from parent processes. If anything |
| 39 # invoking us accidentally masks SIGINT (e.g. by putting a process in the | 40 # invoking us accidentally masks SIGINT (e.g. by putting a process in the |
| 40 # background from a shell script), sending a SIGINT to the child will fail | 41 # background from a shell script), sending a SIGINT to the child will fail |
| 41 # to terminate it. Running this signal handler before execing should fix that | 42 # to terminate it. Running this signal handler before execing should fix that |
| 42 # problem. | 43 # problem. |
| 43 def ResetInterruptHandler(): | 44 def ResetInterruptHandler(): |
| 44 signal.signal(signal.SIGINT, signal.SIG_DFL) | 45 signal.signal(signal.SIGINT, signal.SIG_DFL) |
| 45 | 46 |
| 47 |
| 46 class ReplayError(Exception): | 48 class ReplayError(Exception): |
| 47 """Catch-all exception for the module.""" | 49 """Catch-all exception for the module.""" |
| 48 pass | 50 pass |
| 49 | 51 |
| 52 |
| 50 class ReplayNotFoundError(ReplayError): | 53 class ReplayNotFoundError(ReplayError): |
| 51 def __init__(self, label, path): | 54 def __init__(self, label, path): |
| 52 self.args = (label, path) | 55 self.args = (label, path) |
| 53 | 56 |
| 54 def __str__(self): | 57 def __str__(self): |
| 55 label, path = self.args | 58 label, path = self.args |
| 56 return 'Path does not exist for %s: %s' % (label, path) | 59 return 'Path does not exist for %s: %s' % (label, path) |
| 57 | 60 |
| 61 |
| 58 class ReplayNotStartedError(ReplayError): | 62 class ReplayNotStartedError(ReplayError): |
| 59 pass | 63 pass |
| 60 | 64 |
| 61 | 65 |
| 62 class ReplayServer(object): | 66 class ReplayServer(object): |
| 63 """Start and Stop Web Page Replay. | 67 """Start and Stop Web Page Replay. |
| 64 | 68 |
| 65 Web Page Replay is a proxy that can record and "replay" web pages with | 69 Web Page Replay is a proxy that can record and "replay" web pages with |
| 66 simulated network characteristics -- without having to edit the pages | 70 simulated network characteristics -- without having to edit the pages |
| 67 by hand. With WPR, tests can use "real" web content, and catch | 71 by hand. With WPR, tests can use "real" web content, and catch |
| 68 performance issues that may result from introducing network delays and | 72 performance issues that may result from introducing network delays and |
| 69 bandwidth throttling. | 73 bandwidth throttling. |
| 70 | 74 |
| 71 Example: | 75 Example: |
| 72 with ReplayServer(archive_path): | 76 with ReplayServer(archive_path): |
| 73 self.NavigateToURL(start_url) | 77 self.NavigateToURL(start_url) |
| 74 self.WaitUntil(...) | 78 self.WaitUntil(...) |
| 75 | 79 |
| 76 Environment Variables (for development): | 80 Environment Variables (for development): |
| 77 WPR_ARCHIVE_PATH: path to alternate archive file (e.g. '/tmp/foo.wpr'). | 81 WPR_ARCHIVE_PATH: path to alternate archive file (e.g. '/tmp/foo.wpr'). |
| 78 WPR_RECORD: if set, puts Web Page Replay in record mode instead of replay. | 82 WPR_RECORD: if set, puts Web Page Replay in record mode instead of replay. |
| 79 WPR_REPLAY_DIR: path to alternate Web Page Replay source. | 83 WPR_REPLAY_DIR: path to alternate Web Page Replay source. |
| 80 """ | 84 """ |
| 85 |
| 81 def __init__(self, archive_path, replay_host, http_port, https_port, | 86 def __init__(self, archive_path, replay_host, http_port, https_port, |
| 82 replay_options=None, replay_dir=None, | 87 replay_options=None, replay_dir=None, |
| 83 log_path=None): | 88 log_path=None): |
| 84 """Initialize ReplayServer. | 89 """Initialize ReplayServer. |
| 85 | 90 |
| 86 Args: | 91 Args: |
| 87 archive_path: a path to a specific WPR archive (required). | 92 archive_path: a path to a specific WPR archive (required). |
| 88 replay_options: an iterable of options strings to forward to replay.py. | 93 replay_options: an iterable of options strings to forward to replay.py. |
| 89 replay_dir: directory that has replay.py and related modules. | 94 replay_dir: directory that has replay.py and related modules. |
| 90 log_path: a path to a log file. | 95 log_path: a path to a log file. |
| 91 """ | 96 """ |
| 92 self.archive_path = os.environ.get('WPR_ARCHIVE_PATH', archive_path) | 97 self.archive_path = os.environ.get('WPR_ARCHIVE_PATH', archive_path) |
| 93 self.replay_options = list(replay_options or ()) | 98 self.replay_options = list(replay_options or ()) |
| 94 self.replay_dir = os.environ.get('WPR_REPLAY_DIR', replay_dir or REPLAY_DIR) | 99 self.replay_dir = os.environ.get('WPR_REPLAY_DIR', replay_dir or REPLAY_DIR) |
| 95 self.log_path = log_path or LOG_PATH | 100 self.log_path = log_path or LOG_PATH |
| 96 self._http_port = http_port | 101 self._http_port = http_port |
| 97 self._https_port = https_port | 102 self._https_port = https_port |
| 98 self._replay_host = replay_host | 103 self._replay_host = replay_host |
| 99 | 104 |
| 100 if 'WPR_RECORD' in os.environ and '--record' not in self.replay_options: | 105 if 'WPR_RECORD' in os.environ and '--record' not in self.replay_options: |
| 101 self.replay_options.append('--record') | 106 self.replay_options.append('--record') |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 200 == urllib.urlopen(https_up_url, None, {}).getcode()): | 152 200 == urllib.urlopen(https_up_url, None, {}).getcode()): |
| 148 return True | 153 return True |
| 149 except IOError: | 154 except IOError: |
| 150 time.sleep(1) | 155 time.sleep(1) |
| 151 return False | 156 return False |
| 152 | 157 |
| 153 def StartServer(self): | 158 def StartServer(self): |
| 154 """Start Web Page Replay and verify that it started. | 159 """Start Web Page Replay and verify that it started. |
| 155 | 160 |
| 156 Raises: | 161 Raises: |
| 157 ReplayNotStartedError if Replay start-up fails. | 162 ReplayNotStartedError: if Replay start-up fails. |
| 158 """ | 163 """ |
| 159 cmd_line = [sys.executable, self.replay_py] | 164 cmd_line = [sys.executable, self.replay_py] |
| 160 cmd_line.extend(self.replay_options) | 165 cmd_line.extend(self.replay_options) |
| 161 cmd_line.append(self.archive_path) | 166 cmd_line.append(self.archive_path) |
| 162 self.log_fh = self._OpenLogFile() | 167 self.log_fh = self._OpenLogFile() |
| 163 logging.debug('Starting Web-Page-Replay: %s', cmd_line) | 168 logging.debug('Starting Web-Page-Replay: %s', cmd_line) |
| 164 kwargs = { 'stdout': self.log_fh, 'stderr': subprocess.STDOUT } | 169 kwargs = {'stdout': self.log_fh, 'stderr': subprocess.STDOUT} |
| 165 if sys.platform.startswith('linux') or sys.platform == 'darwin': | 170 if sys.platform.startswith('linux') or sys.platform == 'darwin': |
| 166 kwargs['preexec_fn'] = ResetInterruptHandler | 171 kwargs['preexec_fn'] = ResetInterruptHandler |
| 167 self.replay_process = subprocess.Popen(cmd_line, **kwargs) | 172 self.replay_process = subprocess.Popen(cmd_line, **kwargs) |
| 168 if not self.IsStarted(): | 173 if not self.IsStarted(): |
| 169 log = open(self.log_path).read() | 174 log = open(self.log_path).read() |
| 170 raise ReplayNotStartedError( | 175 raise ReplayNotStartedError( |
| 171 'Web Page Replay failed to start. Log output:\n%s' % log) | 176 'Web Page Replay failed to start. Log output:\n%s' % log) |
| 172 | 177 |
| 173 def StopServer(self): | 178 def StopServer(self): |
| 174 """Stop Web Page Replay.""" | 179 """Stop Web Page Replay.""" |
| 175 if self.replay_process: | 180 if self.replay_process: |
| 176 logging.debug('Stopping Web-Page-Replay') | 181 logging.debug('Trying to stop Web-Page-Replay gracefully') |
| 177 # Use a SIGINT so that it can do graceful cleanup. On Windows, we are left | |
| 178 # with no other option than terminate(). | |
| 179 try: | 182 try: |
| 180 self.replay_process.send_signal(signal.SIGINT) | 183 url = 'http://localhost:%s/web-page-replay-command-exit' |
| 181 except: | 184 urllib.urlopen(url % self._http_port, None, {}) |
| 182 self.replay_process.terminate() | 185 except IOError: |
| 183 self.replay_process.wait() | 186 # IOError is possible because the server might exit without response. |
| 187 pass |
| 188 |
| 189 start_time = time.time() |
| 190 while time.time() - start_time < 10: # Timeout after 10 seconds. |
| 191 if self.replay_process.poll() is not None: |
| 192 break |
| 193 time.sleep(1) |
| 194 else: |
| 195 try: |
| 196 # Use a SIGINT so that it can do graceful cleanup. |
| 197 self.replay_process.send_signal(signal.SIGINT) |
| 198 except: # pylint: disable=W0702 |
| 199 # On Windows, we are left with no other option than terminate(). |
| 200 if 'no-dns_forwarding' not in self.replay_options: |
| 201 logging.warning('DNS configuration might not be restored!') |
| 202 try: |
| 203 self.replay_process.terminate() |
| 204 except: # pylint: disable=W0702 |
| 205 pass |
| 206 self.replay_process.wait() |
| 184 if self.log_fh: | 207 if self.log_fh: |
| 185 self.log_fh.close() | 208 self.log_fh.close() |
| 186 | 209 |
| 187 def __enter__(self): | 210 def __enter__(self): |
| 188 """Add support for with-statement.""" | 211 """Add support for with-statement.""" |
| 189 self.StartServer() | 212 self.StartServer() |
| 190 return self | 213 return self |
| 191 | 214 |
| 192 def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): | 215 def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): |
| 193 """Add support for with-statement.""" | 216 """Add support for with-statement.""" |
| 194 self.StopServer() | 217 self.StopServer() |
| OLD | NEW |