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 5import base64 6import logging 7import urlparse 8 9from metrics import chrome_proxy 10from metrics import loading 11from telemetry.core import util 12from telemetry.page import page_measurement 13 14 15class ChromeProxyLatency(page_measurement.PageMeasurement): 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 MeasurePage(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 30class ChromeProxyDataSaving(page_measurement.PageMeasurement): 31 """Chrome proxy data daving measurement.""" 32 def __init__(self, *args, **kwargs): 33 super(ChromeProxyDataSaving, self).__init__(*args, **kwargs) 34 self._metrics = chrome_proxy.ChromeProxyMetric() 35 36 def WillNavigateToPage(self, page, tab): 37 tab.ClearCache(force=True) 38 self._metrics.Start(page, tab) 39 40 def MeasurePage(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 47class ChromeProxyValidation(page_measurement.PageMeasurement): 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 = chrome_proxy.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 MeasurePage(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 95class 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 105class 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 115class ChromeProxySafebrowsing(ChromeProxyValidation): 116 """Correctness measurement for safebrowsing.""" 117 118 def __init__(self): 119 super(ChromeProxySafebrowsing, self).__init__() 120 121 def WillNavigateToPage(self, page, tab): 122 super(ChromeProxySafebrowsing, self).WillNavigateToPage(page, tab) 123 self._expect_timeout = True 124 125 def AddResults(self, tab, results): 126 self._metrics.AddResultsForSafebrowsing(tab, results) 127 128 129_FAKE_PROXY_AUTH_VALUE = 'aabbccdd3b7579186c1b0620614fdb1f0000ffff' 130_TEST_SERVER = 'chromeproxy-test.appspot.com' 131_TEST_SERVER_DEFAULT_URL = 'http://' + _TEST_SERVER + '/default' 132 133 134# We rely on the chromeproxy-test server to facilitate some of the tests. 135# The test server code is at <TBD location> and runs at _TEST_SERVER 136# 137# The test server allow request to override response status, headers, and 138# body through query parameters. See GetResponseOverrideURL. 139def GetResponseOverrideURL(url, respStatus=0, respHeader="", respBody=""): 140 """ Compose the request URL with query parameters to override 141 the chromeproxy-test server response. 142 """ 143 144 queries = [] 145 if respStatus > 0: 146 queries.append('respStatus=%d' % respStatus) 147 if respHeader: 148 queries.append('respHeader=%s' % base64.b64encode(respHeader)) 149 if respBody: 150 queries.append('respBody=%s' % base64.b64encode(respBody)) 151 if len(queries) == 0: 152 return url 153 "&".join(queries) 154 # url has query already 155 if urlparse.urlparse(url).query: 156 return url + '&' + "&".join(queries) 157 else: 158 return url + '?' + "&".join(queries) 159 160 161class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation): 162 """Correctness measurement for proxy fallback. 163 164 In this test, the probe URL does not return 'OK'. Chrome is expected 165 to use the fallback proxy. 166 """ 167 168 def __init__(self): 169 super(ChromeProxyHTTPFallbackProbeURL, self).__init__() 170 171 def CustomizeBrowserOptions(self, options): 172 super(ChromeProxyHTTPFallbackProbeURL, 173 self).CustomizeBrowserOptions(options) 174 # Use the test server probe URL which returns the response 175 # body as specified by respBody. 176 probe_url = GetResponseOverrideURL( 177 _TEST_SERVER_DEFAULT_URL, 178 respBody='not OK') 179 options.AppendExtraBrowserArgs( 180 '--data-reduction-proxy-probe-url=%s' % probe_url) 181 182 def AddResults(self, tab, results): 183 self._metrics.AddResultsForHTTPFallback(tab, results) 184 185 186# Depends on the fix of http://crbug.com/330342. 187class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation): 188 """Correctness measurement for proxy fallback. 189 190 In this test, the configured proxy is the chromeproxy-test server which 191 will send back a response without the expected Via header. Chrome is 192 expected to use the fallback proxy and add the configured proxy to the 193 bad proxy list. 194 """ 195 196 def __init__(self): 197 super(ChromeProxyHTTPFallbackViaHeader, self).__init__() 198 199 def CustomizeBrowserOptions(self, options): 200 super(ChromeProxyHTTPFallbackViaHeader, 201 self).CustomizeBrowserOptions(options) 202 options.AppendExtraBrowserArgs('--ignore-certificate-errors') 203 options.AppendExtraBrowserArgs( 204 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER) 205 options.AppendExtraBrowserArgs( 206 '--spdy-proxy-auth-value=%s' % _FAKE_PROXY_AUTH_VALUE) 207 208 def AddResults(self, tab, results): 209 proxies = [ 210 _TEST_SERVER + ":80", 211 self._metrics.effective_proxies['fallback'], 212 self._metrics.effective_proxies['direct']] 213 bad_proxies = [_TEST_SERVER + ":80"] 214 self._metrics.AddResultsForHTTPFallback(tab, results, proxies, bad_proxies) 215 216 217class ChromeProxySmoke(ChromeProxyValidation): 218 """Smoke measurement for basic chrome proxy correctness.""" 219 220 def __init__(self): 221 super(ChromeProxySmoke, self).__init__() 222 223 def WillNavigateToPage(self, page, tab): 224 super(ChromeProxySmoke, self).WillNavigateToPage(page, tab) 225 if page.name == 'safebrowsing': 226 self._expect_timeout = True 227 228 def AddResults(self, tab, results): 229 # Map a page name to its AddResults func. 230 page_to_metrics = { 231 'header validation': [self._metrics.AddResultsForHeaderValidation], 232 'compression: image': [ 233 self._metrics.AddResultsForHeaderValidation, 234 self._metrics.AddResultsForDataSaving, 235 ], 236 'compression: javascript': [ 237 self._metrics.AddResultsForHeaderValidation, 238 self._metrics.AddResultsForDataSaving, 239 ], 240 'compression: css': [ 241 self._metrics.AddResultsForHeaderValidation, 242 self._metrics.AddResultsForDataSaving, 243 ], 244 'bypass': [self._metrics.AddResultsForBypass], 245 'safebrowsing': [self._metrics.AddResultsForSafebrowsing], 246 } 247 if not self._page.name in page_to_metrics: 248 raise page_measurement.MeasurementFailure( 249 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % ( 250 self._page.name, page_to_metrics.keys())) 251 for add_result in page_to_metrics[self._page.name]: 252 add_result(tab, results) 253