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 |