Extend the enterprise policy for SSL overrides to the "danger" command
[chromium-blink-merge.git] / tools / chrome_proxy / integration_tests / chrome_proxy_metrics.py
blob793acebbaffca3671462151cea88d3c37c4d9f68
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.
5 import logging
6 import time
8 from common import chrome_proxy_metrics
9 from common import network_metrics
10 from common.chrome_proxy_metrics import ChromeProxyMetricException
11 from telemetry.page import page_test
12 from telemetry.value import scalar
15 class ChromeProxyMetric(network_metrics.NetworkMetric):
16 """A Chrome proxy timeline metric."""
18 def __init__(self):
19 super(ChromeProxyMetric, self).__init__()
20 self.compute_data_saving = True
22 def SetEvents(self, events):
23 """Used for unittest."""
24 self._events = events
26 def ResponseFromEvent(self, event):
27 return chrome_proxy_metrics.ChromeProxyResponse(event)
29 def AddResults(self, tab, results):
30 raise NotImplementedError
32 def AddResultsForDataSaving(self, tab, results):
33 resources_via_proxy = 0
34 resources_from_cache = 0
35 resources_direct = 0
37 super(ChromeProxyMetric, self).AddResults(tab, results)
38 for resp in self.IterResponses(tab):
39 if resp.response.served_from_cache:
40 resources_from_cache += 1
41 if resp.HasChromeProxyViaHeader():
42 resources_via_proxy += 1
43 else:
44 resources_direct += 1
46 if resources_from_cache + resources_via_proxy + resources_direct == 0:
47 raise ChromeProxyMetricException, (
48 'Expected at least one response, but zero responses were received.')
50 results.AddValue(scalar.ScalarValue(
51 results.current_page, 'resources_via_proxy', 'count',
52 resources_via_proxy))
53 results.AddValue(scalar.ScalarValue(
54 results.current_page, 'resources_from_cache', 'count',
55 resources_from_cache))
56 results.AddValue(scalar.ScalarValue(
57 results.current_page, 'resources_direct', 'count', resources_direct))
59 def AddResultsForHeaderValidation(self, tab, results):
60 via_count = 0
62 for resp in self.IterResponses(tab):
63 if resp.IsValidByViaHeader():
64 via_count += 1
65 else:
66 r = resp.response
67 raise ChromeProxyMetricException, (
68 '%s: Via header (%s) is not valid (refer=%s, status=%d)' % (
69 r.url, r.GetHeader('Via'), r.GetHeader('Referer'), r.status))
71 if via_count == 0:
72 raise ChromeProxyMetricException, (
73 'Expected at least one response through the proxy, but zero such '
74 'responses were received.')
75 results.AddValue(scalar.ScalarValue(
76 results.current_page, 'checked_via_header', 'count', via_count))
78 def AddResultsForLatency(self, tab, results):
79 # TODO(bustamante): This is a hack to workaround crbug.com/467174,
80 # once fixed just pull down window.performance.timing object and
81 # reference that everywhere.
82 load_event_start = tab.EvaluateJavaScript(
83 'window.performance.timing.loadEventStart')
84 navigation_start = tab.EvaluateJavaScript(
85 'window.performance.timing.navigationStart')
86 dom_content_loaded_event_start = tab.EvaluateJavaScript(
87 'window.performance.timing.domContentLoadedEventStart')
88 fetch_start = tab.EvaluateJavaScript(
89 'window.performance.timing.fetchStart')
90 request_start = tab.EvaluateJavaScript(
91 'window.performance.timing.requestStart')
92 domain_lookup_end = tab.EvaluateJavaScript(
93 'window.performance.timing.domainLookupEnd')
94 domain_lookup_start = tab.EvaluateJavaScript(
95 'window.performance.timing.domainLookupStart')
96 connect_end = tab.EvaluateJavaScript(
97 'window.performance.timing.connectEnd')
98 connect_start = tab.EvaluateJavaScript(
99 'window.performance.timing.connectStart')
100 response_end = tab.EvaluateJavaScript(
101 'window.performance.timing.responseEnd')
102 response_start = tab.EvaluateJavaScript(
103 'window.performance.timing.responseStart')
105 # NavigationStart relative markers in milliseconds.
106 load_start = (float(load_event_start) - navigation_start)
107 results.AddValue(scalar.ScalarValue(
108 results.current_page, 'load_start', 'ms', load_start))
110 dom_content_loaded_start = (
111 float(dom_content_loaded_event_start) - navigation_start)
112 results.AddValue(scalar.ScalarValue(
113 results.current_page, 'dom_content_loaded_start', 'ms',
114 dom_content_loaded_start))
116 fetch_start = (float(fetch_start) - navigation_start)
117 results.AddValue(scalar.ScalarValue(
118 results.current_page, 'fetch_start', 'ms', fetch_start,
119 important=False))
121 request_start = (float(request_start) - navigation_start)
122 results.AddValue(scalar.ScalarValue(
123 results.current_page, 'request_start', 'ms', request_start,
124 important=False))
126 # Phase measurements in milliseconds.
127 domain_lookup_duration = (float(domain_lookup_end) - domain_lookup_start)
128 results.AddValue(scalar.ScalarValue(
129 results.current_page, 'domain_lookup_duration', 'ms',
130 domain_lookup_duration, important=False))
132 connect_duration = (float(connect_end) - connect_start)
133 results.AddValue(scalar.ScalarValue(
134 results.current_page, 'connect_duration', 'ms', connect_duration,
135 important=False))
137 request_duration = (float(response_start) - request_start)
138 results.AddValue(scalar.ScalarValue(
139 results.current_page, 'request_duration', 'ms', request_duration,
140 important=False))
142 response_duration = (float(response_end) - response_start)
143 results.AddValue(scalar.ScalarValue(
144 results.current_page, 'response_duration', 'ms', response_duration,
145 important=False))
147 def AddResultsForExtraViaHeader(self, tab, results, extra_via_header):
148 extra_via_count = 0
150 for resp in self.IterResponses(tab):
151 if resp.HasChromeProxyViaHeader():
152 if resp.HasExtraViaHeader(extra_via_header):
153 extra_via_count += 1
154 else:
155 raise ChromeProxyMetricException, (
156 '%s: Should have via header %s.' % (resp.response.url,
157 extra_via_header))
159 results.AddValue(scalar.ScalarValue(
160 results.current_page, 'extra_via_header', 'count', extra_via_count))
162 def AddResultsForClientVersion(self, tab, results):
163 via_count = 0
164 for resp in self.IterResponses(tab):
165 r = resp.response
166 if resp.response.status != 200:
167 raise ChromeProxyMetricException, ('%s: Response is not 200: %d' %
168 (r.url, r.status))
169 if not resp.IsValidByViaHeader():
170 raise ChromeProxyMetricException, ('%s: Response missing via header' %
171 (r.url))
172 via_count += 1
174 if via_count == 0:
175 raise ChromeProxyMetricException, (
176 'Expected at least one response through the proxy, but zero such '
177 'responses were received.')
178 results.AddValue(scalar.ScalarValue(
179 results.current_page, 'responses_via_proxy', 'count', via_count))
181 def GetClientTypeFromRequests(self, tab):
182 """Get the Chrome-Proxy client type value from requests made in this tab.
184 Returns:
185 The client type value from the first request made in this tab that
186 specifies a client type in the Chrome-Proxy request header. See
187 ChromeProxyResponse.GetChromeProxyClientType for more details about the
188 Chrome-Proxy client type. Returns None if none of the requests made in
189 this tab specify a client type.
191 for resp in self.IterResponses(tab):
192 client_type = resp.GetChromeProxyClientType()
193 if client_type:
194 return client_type
195 return None
197 def AddResultsForClientType(self, tab, results, client_type,
198 bypass_for_client_type):
199 via_count = 0
200 bypass_count = 0
202 for resp in self.IterResponses(tab):
203 if resp.HasChromeProxyViaHeader():
204 via_count += 1
205 if client_type.lower() == bypass_for_client_type.lower():
206 raise ChromeProxyMetricException, (
207 '%s: Response for client of type "%s" has via header, but should '
208 'be bypassed.' % (resp.response.url, bypass_for_client_type))
209 elif resp.ShouldHaveChromeProxyViaHeader():
210 bypass_count += 1
211 if client_type.lower() != bypass_for_client_type.lower():
212 raise ChromeProxyMetricException, (
213 '%s: Response missing via header. Only "%s" clients should '
214 'bypass for this page, but this client is "%s".' % (
215 resp.response.url, bypass_for_client_type, client_type))
217 if via_count + bypass_count == 0:
218 raise ChromeProxyMetricException, (
219 'Expected at least one response that was eligible to be proxied, but '
220 'zero such responses were received.')
222 results.AddValue(scalar.ScalarValue(
223 results.current_page, 'via', 'count', via_count))
224 results.AddValue(scalar.ScalarValue(
225 results.current_page, 'bypass', 'count', bypass_count))
227 def AddResultsForLoFi(self, tab, results):
228 lo_fi_request_count = 0
229 lo_fi_response_count = 0
231 for resp in self.IterResponses(tab):
232 if resp.HasChromeProxyLoFiRequest():
233 lo_fi_request_count += 1
234 else:
235 raise ChromeProxyMetricException, (
236 '%s: LoFi not in request header.' % (resp.response.url))
238 if resp.HasChromeProxyLoFiResponse():
239 lo_fi_response_count += 1
240 else:
241 raise ChromeProxyMetricException, (
242 '%s: LoFi not in response header.' % (resp.response.url))
244 if resp.content_length > 100:
245 raise ChromeProxyMetricException, (
246 'Image %s is %d bytes. Expecting less than 100 bytes.' %
247 (resp.response.url, resp.content_length))
249 if lo_fi_request_count == 0:
250 raise ChromeProxyMetricException, (
251 'Expected at least one LoFi request, but zero such requests were '
252 'sent.')
253 if lo_fi_response_count == 0:
254 raise ChromeProxyMetricException, (
255 'Expected at least one LoFi response, but zero such responses were '
256 'received.')
258 results.AddValue(scalar.ScalarValue(
259 results.current_page, 'lo_fi_request', 'count', lo_fi_request_count))
260 results.AddValue(scalar.ScalarValue(
261 results.current_page, 'lo_fi_response', 'count', lo_fi_response_count))
262 super(ChromeProxyMetric, self).AddResults(tab, results)
264 def AddResultsForBypass(self, tab, results):
265 bypass_count = 0
267 for resp in self.IterResponses(tab):
268 if resp.HasChromeProxyViaHeader():
269 r = resp.response
270 raise ChromeProxyMetricException, (
271 '%s: Should not have Via header (%s) (refer=%s, status=%d)' % (
272 r.url, r.GetHeader('Via'), r.GetHeader('Referer'), r.status))
273 bypass_count += 1
275 if bypass_count == 0:
276 raise ChromeProxyMetricException, (
277 'Expected at least one response to be bypassed, but zero such '
278 'responses were received.')
279 results.AddValue(scalar.ScalarValue(
280 results.current_page, 'bypass', 'count', bypass_count))
282 def AddResultsForCorsBypass(self, tab, results):
283 eligible_response_count = 0
284 bypass_count = 0
285 bypasses = {}
286 for resp in self.IterResponses(tab):
287 logging.warn('got a resource %s' % (resp.response.url))
289 for resp in self.IterResponses(tab):
290 if resp.ShouldHaveChromeProxyViaHeader():
291 eligible_response_count += 1
292 if not resp.HasChromeProxyViaHeader():
293 bypass_count += 1
294 elif resp.response.status == 502:
295 bypasses[resp.response.url] = 0
297 for resp in self.IterResponses(tab):
298 if resp.ShouldHaveChromeProxyViaHeader():
299 if not resp.HasChromeProxyViaHeader():
300 if resp.response.status == 200:
301 if (bypasses.has_key(resp.response.url)):
302 bypasses[resp.response.url] = bypasses[resp.response.url] + 1
304 for url in bypasses:
305 if bypasses[url] == 0:
306 raise ChromeProxyMetricException, (
307 '%s: Got a 502 without a subsequent 200' % (url))
308 elif bypasses[url] > 1:
309 raise ChromeProxyMetricException, (
310 '%s: Got a 502 and multiple 200s: %d' % (url, bypasses[url]))
311 if bypass_count == 0:
312 raise ChromeProxyMetricException, (
313 'At least one response should be bypassed. '
314 '(eligible_response_count=%d, bypass_count=%d)\n' % (
315 eligible_response_count, bypass_count))
317 results.AddValue(scalar.ScalarValue(
318 results.current_page, 'cors_bypass', 'count', bypass_count))
320 def AddResultsForBlockOnce(self, tab, results):
321 eligible_response_count = 0
322 bypass_count = 0
324 for resp in self.IterResponses(tab):
325 if resp.ShouldHaveChromeProxyViaHeader():
326 eligible_response_count += 1
327 if not resp.HasChromeProxyViaHeader():
328 bypass_count += 1
330 if eligible_response_count <= 1:
331 raise ChromeProxyMetricException, (
332 'There should be more than one DRP eligible response '
333 '(eligible_response_count=%d, bypass_count=%d)\n' % (
334 eligible_response_count, bypass_count))
335 elif bypass_count != 1:
336 raise ChromeProxyMetricException, (
337 'Exactly one response should be bypassed. '
338 '(eligible_response_count=%d, bypass_count=%d)\n' % (
339 eligible_response_count, bypass_count))
340 else:
341 results.AddValue(scalar.ScalarValue(
342 results.current_page, 'eligible_responses', 'count',
343 eligible_response_count))
344 results.AddValue(scalar.ScalarValue(
345 results.current_page, 'bypass', 'count', bypass_count))
347 def AddResultsForSafebrowsingOn(self, tab, results):
348 results.AddValue(scalar.ScalarValue(
349 results.current_page, 'safebrowsing', 'timeout responses', 1))
351 def AddResultsForSafebrowsingOff(self, tab, results):
352 response_count = 0
353 for resp in self.IterResponses(tab):
354 # Data reduction proxy should return the real response for sites with
355 # malware.
356 response_count += 1
357 if not resp.HasChromeProxyViaHeader():
358 r = resp.response
359 raise ChromeProxyMetricException, (
360 '%s: Safebrowsing feature should be off for desktop and webview.\n'
361 'Reponse: status=(%d, %s)\nHeaders:\n %s' % (
362 r.url, r.status, r.status_text, r.headers))
364 if response_count == 0:
365 raise ChromeProxyMetricException, (
366 'Safebrowsing test failed: No valid responses received')
368 results.AddValue(scalar.ScalarValue(
369 results.current_page, 'safebrowsing', 'responses', response_count))
371 def AddResultsForHTTPFallback(self, tab, results):
372 via_fallback_count = 0
374 for resp in self.IterResponses(tab):
375 if resp.ShouldHaveChromeProxyViaHeader():
376 # All responses should have come through the HTTP fallback proxy, which
377 # means that they should have the via header, and if a remote port is
378 # defined, it should be port 80.
379 if (not resp.HasChromeProxyViaHeader() or
380 (resp.remote_port and resp.remote_port != 80)):
381 r = resp.response
382 raise ChromeProxyMetricException, (
383 '%s: Should have come through the fallback proxy.\n'
384 'Reponse: remote_port=%s status=(%d, %s)\nHeaders:\n %s' % (
385 r.url, str(resp.remote_port), r.status, r.status_text,
386 r.headers))
387 via_fallback_count += 1
389 if via_fallback_count == 0:
390 raise ChromeProxyMetricException, (
391 'Expected at least one response through the fallback proxy, but zero '
392 'such responses were received.')
393 results.AddValue(scalar.ScalarValue(
394 results.current_page, 'via_fallback', 'count', via_fallback_count))
396 def AddResultsForHTTPToDirectFallback(self, tab, results,
397 fallback_response_host):
398 via_fallback_count = 0
399 bypass_count = 0
400 responses = self.IterResponses(tab)
402 # The first response(s) coming from fallback_response_host should be
403 # through the HTTP fallback proxy.
404 resp = next(responses, None)
405 while resp and fallback_response_host in resp.response.url:
406 if fallback_response_host in resp.response.url:
407 if (not resp.HasChromeProxyViaHeader() or resp.remote_port != 80):
408 r = resp.response
409 raise ChromeProxyMetricException, (
410 'Response for %s should have come through the fallback proxy.\n'
411 'Response: remote_port=%s status=(%d, %s)\nHeaders:\n %s' % (
412 r.url, str(resp.remote_port), r.status, r.status_text,
413 r.headers))
414 else:
415 via_fallback_count += 1
416 resp = next(responses, None)
418 # All other responses should be bypassed.
419 while resp:
420 if resp.HasChromeProxyViaHeader():
421 r = resp.response
422 raise ChromeProxyMetricException, (
423 'Response for %s should not have via header.\n'
424 'Response: status=(%d, %s)\nHeaders:\n %s' % (
425 r.url, r.status, r.status_text, r.headers))
426 else:
427 bypass_count += 1
428 resp = next(responses, None)
430 # At least one response should go through the http proxy and be bypassed.
431 if via_fallback_count == 0 or bypass_count == 0:
432 raise ChromeProxyMetricException(
433 'There should be at least one response through the fallback proxy '
434 '(actual %s) and at least one bypassed response (actual %s)' %
435 (via_fallback_count, bypass_count))
437 results.AddValue(scalar.ScalarValue(
438 results.current_page, 'via_fallback', 'count', via_fallback_count))
439 results.AddValue(scalar.ScalarValue(
440 results.current_page, 'bypass', 'count', bypass_count))
442 def AddResultsForReenableAfterBypass(
443 self, tab, results, bypass_seconds_min, bypass_seconds_max):
444 """Verify results for a re-enable after bypass test.
446 Args:
447 tab: the tab for the test.
448 results: the results object to add the results values to.
449 bypass_seconds_min: the minimum duration of the bypass.
450 bypass_seconds_max: the maximum duration of the bypass.
452 bypass_count = 0
453 via_count = 0
455 for resp in self.IterResponses(tab):
456 if resp.HasChromeProxyViaHeader():
457 r = resp.response
458 raise ChromeProxyMetricException, (
459 'Response for %s should not have via header.\n'
460 'Reponse: status=(%d, %s)\nHeaders:\n %s' % (
461 r.url, r.status, r.status_text, r.headers))
462 else:
463 bypass_count += 1
465 # Wait until 30 seconds before the bypass should expire, and fetch a page.
466 # It should not have the via header because the proxy should still be
467 # bypassed.
468 time.sleep(bypass_seconds_min - 30)
470 tab.ClearCache(force=True)
471 before_metrics = ChromeProxyMetric()
472 before_metrics.Start(results.current_page, tab)
473 tab.Navigate('http://chromeproxy-test.appspot.com/default')
474 tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 10)
475 before_metrics.Stop(results.current_page, tab)
477 for resp in before_metrics.IterResponses(tab):
478 if resp.HasChromeProxyViaHeader():
479 r = resp.response
480 raise ChromeProxyMetricException, (
481 'Response for %s should not have via header; proxy should still '
482 'be bypassed.\nReponse: status=(%d, %s)\nHeaders:\n %s' % (
483 r.url, r.status, r.status_text, r.headers))
484 else:
485 bypass_count += 1
486 if bypass_count == 0:
487 raise ChromeProxyMetricException, (
488 'Expected at least one response to be bypassed before the bypass '
489 'expired, but zero such responses were received.')
491 # Wait until 30 seconds after the bypass should expire, and fetch a page. It
492 # should have the via header since the proxy should no longer be bypassed.
493 time.sleep((bypass_seconds_max + 30) - (bypass_seconds_min - 30))
495 tab.ClearCache(force=True)
496 after_metrics = ChromeProxyMetric()
497 after_metrics.Start(results.current_page, tab)
498 tab.Navigate('http://chromeproxy-test.appspot.com/default')
499 tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 10)
500 after_metrics.Stop(results.current_page, tab)
502 for resp in after_metrics.IterResponses(tab):
503 if not resp.HasChromeProxyViaHeader():
504 r = resp.response
505 raise ChromeProxyMetricException, (
506 'Response for %s should have via header; proxy should no longer '
507 'be bypassed.\nReponse: status=(%d, %s)\nHeaders:\n %s' % (
508 r.url, r.status, r.status_text, r.headers))
509 else:
510 via_count += 1
511 if via_count == 0:
512 raise ChromeProxyMetricException, (
513 'Expected at least one response through the proxy after the bypass '
514 'expired, but zero such responses were received.')
516 results.AddValue(scalar.ScalarValue(
517 results.current_page, 'bypass', 'count', bypass_count))
518 results.AddValue(scalar.ScalarValue(
519 results.current_page, 'via', 'count', via_count))