blob: eda55e03dba27237aab59aec8e3c7ba95963eb0b [file] [log] [blame]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import common_util
import emulation
import sandwich_runner
import task_manager
def NetworkSimulationTransformer(network_condition):
"""Creates a function that accepts a SandwichRunner as a parameter and sets
network emulation options on it.
Args:
network_condition: The network condition to apply to the sandwich runner.
Returns:
A callback transforming the SandwichRunner given in argument accordingly
"""
assert network_condition in emulation.NETWORK_CONDITIONS
def Transformer(runner):
assert isinstance(runner, sandwich_runner.SandwichRunner)
runner.network_condition = network_condition
return Transformer
def FilterOutDataAndIncompleteRequests(requests):
for request in filter(lambda r: not r.IsDataRequest(), requests):
# The protocol is only known once the response has been received. But the
# trace recording might have been stopped with still some JavaScript
# originated requests that have not received any responses yet.
if request.protocol is None:
assert not request.HasReceivedResponse()
continue
if request.protocol in {'about'}:
continue
if request.protocol not in {'http/0.9', 'http/1.0', 'http/1.1'}:
raise RuntimeError('Unknown request protocol {}'.format(request.protocol))
yield request
class RequestOutcome:
All, ServedFromCache, NotServedFromCache, Post = range(4)
def ListUrlRequests(trace, request_kind):
"""Lists requested URLs from a trace.
Args:
trace: (loading_trace.LoadingTrace) loading trace.
request_kind: RequestOutcome.* indicating the subset of requests to output.
Returns:
set([str])
"""
urls = set()
for request_event in FilterOutDataAndIncompleteRequests(
trace.request_track.GetEvents()):
if (request_kind == RequestOutcome.ServedFromCache and
request_event.from_disk_cache):
urls.add(request_event.url)
elif (request_kind == RequestOutcome.Post and
request_event.method.upper().strip() == 'POST'):
urls.add(request_event.url)
elif (request_kind == RequestOutcome.NotServedFromCache and
not request_event.from_disk_cache):
urls.add(request_event.url)
elif request_kind == RequestOutcome.All:
urls.add(request_event.url)
return urls
def PatchWprEntryToBeCached(wpr_url_entry):
"""Patches a WprUrlEntry to ensure the resources to go into the HTTP cache and
avoid invalidation and revalidations.
Args:
wpr_url_entry: Wpr url entry of the resource to put into the cache.
"""
MAX_AGE = 10 * 365 * 24 * 60 * 60
CACHE_CONTROL = 'public, max-age={}'.format(MAX_AGE)
# TODO(gabadie): may need to patch Last-Modified and If-Modified-Since.
# TODO(gabadie): may need to delete ETag.
# TODO(gabadie): may need to take care of x-cache.
#
# Override the cache-control header to set the resources max age to MAX_AGE.
#
# Important note: Some resources holding sensitive information might have
# cache-control set to no-store which allow the resource to be cached but
# not cached in the file system. NoState-Prefetch is going to take care of
# this case. But in here, to simulate NoState-Prefetch, we don't have other
# choices but save absolutely all cached resources on disk so they survive
# after killing chrome for cache save, modification and push.
wpr_url_entry.SetResponseHeader('cache-control', CACHE_CONTROL)
# TODO(gabadie): May need to extend Vary blacklist (referer?)
#
# All of these Vary and Pragma possibilities need to be removed from
# response headers in order for Chrome to store a resource in HTTP cache and
# not to invalidate it.
wpr_url_entry.RemoveResponseHeaderDirectives('vary', {'*', 'cookie'})
wpr_url_entry.RemoveResponseHeaderDirectives('pragma', {'no-cache'})
class SandwichCommonBuilder(task_manager.Builder):
"""A builder for a graph of tasks, each prepares or invokes a SandwichRunner.
"""
def __init__(self, android_device, url, output_directory,
output_subdirectory):
"""Constructor.
Args:
android_device: The android DeviceUtils to run sandwich on or None to run
it locally.
url: URL to benchmark.
output_directory: As in task_manager.Builder.__init__
output_subdirectory: As in task_manager.Builder.__init__
"""
task_manager.Builder.__init__(self, output_directory, output_subdirectory)
self._android_device = android_device
self._url = url
self.default_final_tasks = []
self.original_wpr_task = None
self.original_wpr_recording_trace_path = None
def CreateSandwichRunner(self):
"""Create a runner for non benchmark purposes."""
runner = sandwich_runner.SandwichRunner()
runner.url = self._url
runner.android_device = self._android_device
return runner
def PopulateWprRecordingTask(self):
"""Records the original WPR archive."""
@self.RegisterTask('common/webpages.wpr')
def BuildOriginalWpr():
common_util.EnsureParentDirectoryExists(BuildOriginalWpr.path)
runner = self.CreateSandwichRunner()
runner.wpr_archive_path = BuildOriginalWpr.path
runner.wpr_record = True
runner.output_dir = BuildOriginalWpr.run_path
runner.Run()
BuildOriginalWpr.run_path = BuildOriginalWpr.path[:-4] + '-run'
self.original_wpr_task = BuildOriginalWpr
self.original_wpr_recording_trace_path = os.path.join(
BuildOriginalWpr.run_path, '0', sandwich_runner.TRACE_FILENAME)