1 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import base64 6 import logging 7 import urlparse 8 9 from integration_tests import chrome_proxy_metrics as metrics 10 from metrics import loading 11 from telemetry.core import util 12 from telemetry.page import page_test 13 14 15 class ChromeProxyLatency(page_test.PageTest): 16 """Chrome proxy latency measurement.""" 17 18 def __init__(self, *args, **kwargs): 19 super(ChromeProxyLatency, self).__init__(*args, **kwargs) 20 21 def WillNavigateToPage(self, page, tab): 22 tab.ClearCache(force=True) 23 24 def ValidateAndMeasurePage(self, page, tab, results): 25 # Wait for the load event. 26 tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300) 27 loading.LoadingMetric().AddResults(tab, results) 28 29 30 class ChromeProxyDataSaving(page_test.PageTest): 31 """Chrome proxy data daving measurement.""" 32 def __init__(self, *args, **kwargs): 33 super(ChromeProxyDataSaving, self).__init__(*args, **kwargs) 34 self._metrics = metrics.ChromeProxyMetric() 35 36 def WillNavigateToPage(self, page, tab): 37 tab.ClearCache(force=True) 38 self._metrics.Start(page, tab) 39 40 def ValidateAndMeasurePage(self, page, tab, results): 41 # Wait for the load event. 42 tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300) 43 self._metrics.Stop(page, tab) 44 self._metrics.AddResultsForDataSaving(tab, results) 45 46 47 class ChromeProxyValidation(page_test.PageTest): 48 """Base class for all chrome proxy correctness measurements.""" 49 50 def __init__(self, restart_after_each_page=False): 51 super(ChromeProxyValidation, self).__init__( 52 needs_browser_restart_after_each_page=restart_after_each_page) 53 self._metrics = metrics.ChromeProxyMetric() 54 self._page = None 55 # Whether a timeout exception is expected during the test. 56 self._expect_timeout = False 57 58 def CustomizeBrowserOptions(self, options): 59 # Enable the chrome proxy (data reduction proxy). 60 options.AppendExtraBrowserArgs('--enable-spdy-proxy-auth') 61 62 def WillNavigateToPage(self, page, tab): 63 tab.ClearCache(force=True) 64 assert self._metrics 65 self._metrics.Start(page, tab) 66 67 def ValidateAndMeasurePage(self, page, tab, results): 68 self._page = page 69 # Wait for the load event. 70 tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300) 71 assert self._metrics 72 self._metrics.Stop(page, tab) 73 self.AddResults(tab, results) 74 75 def AddResults(self, tab, results): 76 raise NotImplementedError 77 78 def StopBrowserAfterPage(self, browser, page): # pylint: disable=W0613 79 if hasattr(page, 'restart_after') and page.restart_after: 80 return True 81 return False 82 83 def RunNavigateSteps(self, page, tab): 84 # The redirect from safebrowsing causes a timeout. Ignore that. 85 try: 86 super(ChromeProxyValidation, self).RunNavigateSteps(page, tab) 87 except util.TimeoutException, e: 88 if self._expect_timeout: 89 logging.warning('Navigation timeout on page %s', 90 page.name if page.name else page.url) 91 else: 92 raise e 93 94 95 class ChromeProxyHeaders(ChromeProxyValidation): 96 """Correctness measurement for response headers.""" 97 98 def __init__(self): 99 super(ChromeProxyHeaders, self).__init__(restart_after_each_page=True) 100 101 def AddResults(self, tab, results): 102 self._metrics.AddResultsForHeaderValidation(tab, results) 103 104 105 class ChromeProxyBypass(ChromeProxyValidation): 106 """Correctness measurement for bypass responses.""" 107 108 def __init__(self): 109 super(ChromeProxyBypass, self).__init__(restart_after_each_page=True) 110 111 def AddResults(self, tab, results): 112 self._metrics.AddResultsForBypass(tab, results) 113 114 115 class ChromeProxyBlockOnce(ChromeProxyValidation): 116 """Correctness measurement for block-once responses.""" 117 118 def __init__(self): 119 super(ChromeProxyBlockOnce, self).__init__(restart_after_each_page=True) 120 121 def AddResults(self, tab, results): 122 self._metrics.AddResultsForBlockOnce(tab, results) 123 124 125 class ChromeProxySafebrowsing(ChromeProxyValidation): 126 """Correctness measurement for safebrowsing.""" 127 128 def __init__(self): 129 super(ChromeProxySafebrowsing, self).__init__() 130 131 def WillNavigateToPage(self, page, tab): 132 super(ChromeProxySafebrowsing, self).WillNavigateToPage(page, tab) 133 self._expect_timeout = True 134 135 def AddResults(self, tab, results): 136 self._metrics.AddResultsForSafebrowsing(tab, results) 137 138 139 _FAKE_PROXY_AUTH_VALUE = 'aabbccdd3b7579186c1b0620614fdb1f0000ffff' 140 _TEST_SERVER = 'chromeproxy-test.appspot.com' 141 _TEST_SERVER_DEFAULT_URL = 'http://' + _TEST_SERVER + '/default' 142 143 144 # We rely on the chromeproxy-test server to facilitate some of the tests. 145 # The test server code is at <TBD location> and runs at _TEST_SERVER 146 # 147 # The test server allow request to override response status, headers, and 148 # body through query parameters. See GetResponseOverrideURL. 149 def GetResponseOverrideURL(url, respStatus=0, respHeader="", respBody=""): 150 """ Compose the request URL with query parameters to override 151 the chromeproxy-test server response. 152 """ 153 154 queries = [] 155 if respStatus > 0: 156 queries.append('respStatus=%d' % respStatus) 157 if respHeader: 158 queries.append('respHeader=%s' % base64.b64encode(respHeader)) 159 if respBody: 160 queries.append('respBody=%s' % base64.b64encode(respBody)) 161 if len(queries) == 0: 162 return url 163 "&".join(queries) 164 # url has query already 165 if urlparse.urlparse(url).query: 166 return url + '&' + "&".join(queries) 167 else: 168 return url + '?' + "&".join(queries) 169 170 171 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation): 172 """Correctness measurement for proxy fallback. 173 174 In this test, the probe URL does not return 'OK'. Chrome is expected 175 to use the fallback proxy. 176 """ 177 178 def __init__(self): 179 super(ChromeProxyHTTPFallbackProbeURL, self).__init__() 180 181 def CustomizeBrowserOptions(self, options): 182 super(ChromeProxyHTTPFallbackProbeURL, 183 self).CustomizeBrowserOptions(options) 184 # Use the test server probe URL which returns the response 185 # body as specified by respBody. 186 probe_url = GetResponseOverrideURL( 187 _TEST_SERVER_DEFAULT_URL, 188 respBody='not OK') 189 options.AppendExtraBrowserArgs( 190 '--data-reduction-proxy-probe-url=%s' % probe_url) 191 192 def AddResults(self, tab, results): 193 self._metrics.AddResultsForHTTPFallback(tab, results) 194 195 196 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation): 197 """Correctness measurement for proxy fallback. 198 199 In this test, the configured proxy is the chromeproxy-test server which 200 will send back a response without the expected Via header. Chrome is 201 expected to use the fallback proxy and add the configured proxy to the 202 bad proxy list. 203 """ 204 205 def __init__(self): 206 super(ChromeProxyHTTPFallbackViaHeader, self).__init__() 207 208 def CustomizeBrowserOptions(self, options): 209 super(ChromeProxyHTTPFallbackViaHeader, 210 self).CustomizeBrowserOptions(options) 211 options.AppendExtraBrowserArgs('--ignore-certificate-errors') 212 options.AppendExtraBrowserArgs( 213 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER) 214 options.AppendExtraBrowserArgs( 215 '--spdy-proxy-auth-value=%s' % _FAKE_PROXY_AUTH_VALUE) 216 217 def AddResults(self, tab, results): 218 proxies = [ 219 _TEST_SERVER + ":80", 220 self._metrics.effective_proxies['fallback'], 221 self._metrics.effective_proxies['direct']] 222 bad_proxies = [_TEST_SERVER + ":80", metrics.PROXY_SETTING_HTTP] 223 self._metrics.AddResultsForHTTPFallback(tab, results, proxies, bad_proxies) 224 225 226 class ChromeProxyClientVersion(ChromeProxyValidation): 227 """Correctness measurement for version directives in Chrome-Proxy header. 228 229 The test verifies that the version information provided in the Chrome-Proxy 230 request header overrides any version, if specified, that is provided in the 231 user agent string. 232 """ 233 234 def __init__(self): 235 super(ChromeProxyClientVersion, self).__init__() 236 237 def CustomizeBrowserOptions(self, options): 238 super(ChromeProxyClientVersion, 239 self).CustomizeBrowserOptions(options) 240 options.AppendExtraBrowserArgs('--user-agent="Chrome/32.0.1700.99"') 241 242 def AddResults(self, tab, results): 243 self._metrics.AddResultsForClientVersion(tab, results) 244 245 246 class ChromeProxySmoke(ChromeProxyValidation): 247 """Smoke measurement for basic chrome proxy correctness.""" 248 249 def __init__(self): 250 super(ChromeProxySmoke, self).__init__() 251 252 def WillNavigateToPage(self, page, tab): 253 super(ChromeProxySmoke, self).WillNavigateToPage(page, tab) 254 if page.name == 'safebrowsing': 255 self._expect_timeout = True 256 257 def AddResults(self, tab, results): 258 # Map a page name to its AddResults func. 259 page_to_metrics = { 260 'header validation': [self._metrics.AddResultsForHeaderValidation], 261 'compression: image': [ 262 self._metrics.AddResultsForHeaderValidation, 263 self._metrics.AddResultsForDataSaving, 264 ], 265 'compression: javascript': [ 266 self._metrics.AddResultsForHeaderValidation, 267 self._metrics.AddResultsForDataSaving, 268 ], 269 'compression: css': [ 270 self._metrics.AddResultsForHeaderValidation, 271 self._metrics.AddResultsForDataSaving, 272 ], 273 'bypass': [self._metrics.AddResultsForBypass], 274 'safebrowsing': [self._metrics.AddResultsForSafebrowsing], 275 } 276 if not self._page.name in page_to_metrics: 277 raise page_test.MeasurementFailure( 278 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % ( 279 self._page.name, page_to_metrics.keys())) 280 for add_result in page_to_metrics[self._page.name]: 281 add_result(tab, results) 282