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 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 200 == urllib.urlopen(https_up_url, None, {}).getcode()): | 153 200 == urllib.urlopen(https_up_url, None, {}).getcode()): |
149 return True | 154 return True |
150 except IOError: | 155 except IOError: |
151 time.sleep(1) | 156 time.sleep(1) |
152 return False | 157 return False |
153 | 158 |
154 def StartServer(self): | 159 def StartServer(self): |
155 """Start Web Page Replay and verify that it started. | 160 """Start Web Page Replay and verify that it started. |
156 | 161 |
157 Raises: | 162 Raises: |
158 ReplayNotStartedError if Replay start-up fails. | 163 ReplayNotStartedError: if Replay start-up fails. |
159 """ | 164 """ |
160 cmd_line = [sys.executable, self.replay_py] | 165 cmd_line = [sys.executable, self.replay_py] |
161 cmd_line.extend(self.replay_options) | 166 cmd_line.extend(self.replay_options) |
162 cmd_line.append(self.archive_path) | 167 cmd_line.append(self.archive_path) |
163 self.log_fh = self._OpenLogFile() | 168 self.log_fh = self._OpenLogFile() |
164 logging.debug('Starting Web-Page-Replay: %s', cmd_line) | 169 logging.debug('Starting Web-Page-Replay: %s', cmd_line) |
165 kwargs = { 'stdout': self.log_fh, 'stderr': subprocess.STDOUT } | 170 kwargs = {'stdout': self.log_fh, 'stderr': subprocess.STDOUT} |
166 if sys.platform.startswith('linux') or sys.platform == 'darwin': | 171 if sys.platform.startswith('linux') or sys.platform == 'darwin': |
167 kwargs['preexec_fn'] = ResetInterruptHandler | 172 kwargs['preexec_fn'] = ResetInterruptHandler |
168 self.replay_process = subprocess.Popen(cmd_line, **kwargs) | 173 self.replay_process = subprocess.Popen(cmd_line, **kwargs) |
169 if not self.IsStarted(): | 174 if not self.IsStarted(): |
170 log = open(self.log_path).read() | 175 log = open(self.log_path).read() |
171 raise ReplayNotStartedError( | 176 raise ReplayNotStartedError( |
172 'Web Page Replay failed to start. Log output:\n%s' % log) | 177 'Web Page Replay failed to start. Log output:\n%s' % log) |
173 | 178 |
174 def StopServer(self): | 179 def StopServer(self): |
175 """Stop Web Page Replay.""" | 180 """Stop Web Page Replay.""" |
176 if self.replay_process: | 181 if self.replay_process: |
177 logging.debug('Stopping Web-Page-Replay') | 182 logging.debug('Trying to stop Web-Page-Replay gracefully') |
178 # Use a SIGINT so that it can do graceful cleanup. On Windows, we are left | |
179 # with no other option than terminate(). | |
180 try: | 183 try: |
181 self.replay_process.send_signal(signal.SIGINT) | 184 url = 'http://localhost:%s/web-page-replay-command-exit' |
182 except: | 185 urllib.urlopen(url % self._http_port, None, {}) |
183 self.replay_process.terminate() | 186 except IOError: |
184 self.replay_process.wait() | 187 # IOError is possible because the server might exit without response. |
188 pass | |
189 | |
190 start_time = time.time() | |
191 while time.time() - start_time < 10: # Timeout after 10 seconds. | |
192 if self.replay_process.poll() is not None: | |
193 break | |
194 time.sleep(1) | |
195 else: | |
196 try: | |
197 # Use a SIGINT so that it can do graceful cleanup. | |
kkania
2013/08/29 15:30:14
would it be simpler at this point to just do a for
chrisgao (Use stgao instead)
2013/08/29 20:54:26
As discussed offline, keep the current approach.
| |
198 self.replay_process.send_signal(signal.SIGINT) | |
199 except: # pylint: disable=W0702 | |
200 # On Windows, we are left with no other option than terminate(). | |
201 if 'no-dns_forwarding' not in self.replay_options: | |
202 logging.warning('DNS configuration might not be restored!') | |
203 try: | |
204 self.replay_process.terminate() | |
205 except: # pylint: disable=W0702 | |
206 pass | |
207 self.replay_process.wait() | |
185 if self.log_fh: | 208 if self.log_fh: |
186 self.log_fh.close() | 209 self.log_fh.close() |
187 | 210 |
188 def __enter__(self): | 211 def __enter__(self): |
189 """Add support for with-statement.""" | 212 """Add support for with-statement.""" |
190 self.StartServer() | 213 self.StartServer() |
191 return self | 214 return self |
192 | 215 |
193 def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): | 216 def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): |
194 """Add support for with-statement.""" | 217 """Add support for with-statement.""" |
195 self.StopServer() | 218 self.StopServer() |
OLD | NEW |