| Index: telemetry/telemetry/internal/platform/network_controller_backend.py
|
| diff --git a/telemetry/telemetry/internal/platform/network_controller_backend.py b/telemetry/telemetry/internal/platform/network_controller_backend.py
|
| index 57cc37d3679cb5f522fc8b86a85d668bc805eccd..5f4422e20b47e7dd731c5447688b8b7849dfd909 100644
|
| --- a/telemetry/telemetry/internal/platform/network_controller_backend.py
|
| +++ b/telemetry/telemetry/internal/platform/network_controller_backend.py
|
| @@ -27,28 +27,118 @@ class NetworkControllerBackend(object):
|
|
|
| def __init__(self, platform_backend):
|
| self._platform_backend = platform_backend
|
| - self._browser_backend = None
|
| - self._active_replay_args = {}
|
| - self._pending_replay_args = {}
|
| + self._wpr_mode = None
|
| + self._netsim = None
|
| + self._extra_wpr_args = None
|
| + self._wpr_port_pairs = None
|
| + self._archive_path = None
|
| + self._make_javascript_deterministic = None
|
| self._forwarder = None
|
| self._wpr_server = None
|
|
|
| - def SetReplayArgs(self, archive_path, wpr_mode, netsim, extra_wpr_args,
|
| - make_javascript_deterministic=False):
|
| - """Save the arguments needed for replay.
|
| + # TODO(perezju) Remove when SetReplayArgs is gone.
|
| + self._pending_replay_args = None
|
| +
|
| + @property
|
| + def is_open(self):
|
| + return self._wpr_mode is not None
|
| +
|
| + @property
|
| + def host_ip(self):
|
| + return self._platform_backend.forwarder_factory.host_ip
|
| +
|
| + @property
|
| + def wpr_mode(self):
|
| + return self._wpr_mode
|
|
|
| - To make the settings effective, this call must be followed by a call
|
| - to UpdateReplay.
|
| + @property
|
| + def wpr_device_ports(self):
|
| + try:
|
| + return self._forwarder.port_pairs.remote_ports
|
| + except AttributeError:
|
| + return None
|
| +
|
| + @property
|
| + def wpr_http_device_port(self):
|
| + # TODO(perezju): Remove and switch clients to wpr_device_ports.http
|
| + return self.wpr_device_ports.http
|
| +
|
| + @property
|
| + def wpr_https_device_port(self):
|
| + # TODO(perezju): Remove and switch clients to wpr_device_ports.https
|
| + return self.wpr_device_ports.https
|
| +
|
| + def Open(self, wpr_mode, netsim, extra_wpr_args):
|
| + """Configure and prepare target platform for network control.
|
| +
|
| + This may, e.g., install test certificates and perform any needed setup
|
| + on the target platform.
|
| +
|
| + After network interactions are over, clients should call the Close method.
|
| +
|
| + Args:
|
| + wpr_mode: a mode for web page replay; available modes are
|
| + wpr_modes.WPR_OFF, wpr_modes.APPEND, wpr_modes.WPR_REPLAY, or
|
| + wpr_modes.WPR_RECORD.
|
| + netsim: a net_config string (e.g. 'dialup', '3g', 'dsl', 'cable', 'fios'),
|
| + may be None.
|
| + extra_wpr_args: an list of extra arguments for web page replay.
|
| + """
|
| + assert not self.is_open, 'Network controller is already open'
|
| + self._wpr_mode = wpr_mode
|
| + self._netsim = netsim
|
| + self._extra_wpr_args = extra_wpr_args
|
| +
|
| + # TODO(perezju): Determine correct ports for different platform backends,
|
| + # and install test certificates if needed.
|
| + self._wpr_port_pairs = forwarders.PortPairs(
|
| + http=forwarders.PortPair(0, 0),
|
| + https=forwarders.PortPair(0, 0),
|
| + dns=forwarders.PortPair(0, 0))
|
| +
|
| + def Close(self):
|
| + """Undo changes in the target platform used for network control.
|
| +
|
| + Implicitly stops replay if currently active.
|
| + """
|
| + # TODO(perezju): Uninstall test certificates if needed.
|
| + self.StopReplay()
|
| + self._make_javascript_deterministic = None
|
| + self._archive_path = None
|
| + self._wpr_port_pairs = None
|
| + self._extra_wpr_args = None
|
| + self._netsim = None
|
| + self._wpr_mode = None
|
| +
|
| + def StartReplay(self, archive_path, make_javascript_deterministic=False):
|
| + """Start web page replay from a given replay archive.
|
| +
|
| + Starts as needed, and reuses if possible, the replay server on the host and
|
| + a forwarder from the host to the target platform.
|
| +
|
| + Implementation details
|
| + ----------------------
|
| +
|
| + The local host is where Telemetry is run. The remote is host where
|
| + the target application is run. The local and remote hosts may be
|
| + the same (e.g., testing a desktop browser) or different (e.g., testing
|
| + an android browser).
|
| +
|
| + A replay server is started on the local host using the local ports, while
|
| + a forwarder ties the local to the remote ports.
|
| +
|
| + Both local and remote ports may be zero. In that case they are determined
|
| + by the replay server and the forwarder respectively. Setting dns to None
|
| + disables DNS traffic.
|
|
|
| Args:
|
| archive_path: a path to a specific WPR archive.
|
| - wpr_mode: one of wpr_modes.WPR_OFF, wpr_modes.WPR_APPEND,
|
| - wpr_modes.WPR_REPLAY, or wpr_modes.WPR_RECORD.
|
| - netsim: a net_config string ('dialup', '3g', 'dsl', 'cable', or 'fios').
|
| - extra_wpr_args: a list of additional replay args (or an empty list).
|
| make_javascript_deterministic: True if replay should inject a script
|
| to make JavaScript behave deterministically (e.g., override Date()).
|
| """
|
| + assert self.is_open, 'Network controller is not open'
|
| + if self._wpr_mode == wpr_modes.WPR_OFF:
|
| + return
|
| if not archive_path:
|
| # TODO(slamm, tonyg): Ideally, replay mode should be stopped when there is
|
| # no archive path. However, if the replay server already started, and
|
| @@ -57,160 +147,123 @@ class NetworkControllerBackend(object):
|
| # replay server forwards requests to it. (Chrome is configured to use
|
| # fixed ports fo all HTTP/HTTPS requests.)
|
| return
|
| - self._pending_replay_args = dict(
|
| - archive_path=archive_path,
|
| - wpr_mode=wpr_mode,
|
| - netsim=netsim,
|
| - extra_wpr_args=extra_wpr_args,
|
| - make_javascript_deterministic=make_javascript_deterministic)
|
| - # TODO(slamm): Update replay here when the browser_backend dependencies
|
| - # are moved to the platform. https://crbug.com/423962
|
| - # |self._pending_replay_args| can be removed at that time.
|
| -
|
| - def UpdateReplay(self, browser_backend=None):
|
| - """Start or reuse Web Page Replay.
|
| -
|
| - UpdateReplay must be called after every call to SetReplayArgs.
|
| -
|
| - TODO(slamm): Update replay in SetReplayArgs once the browser_backend
|
| - dependencies move to platform. https://crbug.com/423962
|
| - browser_backend properties used:
|
| - - Input: wpr_port_pairs
|
| - - Output: wpr_port_pairs (browser uses for --testing-fixed-* flags).
|
| - Args:
|
| - browser_backend: instance of telemetry.core.backends.browser_backend
|
| - """
|
| - if not self._pending_replay_args:
|
| - # In some cases (e.g., unit tests), the browser is used without replay.
|
| - return
|
| - if self._pending_replay_args == self._active_replay_args:
|
| - return
|
| -
|
| - pending_archive_path = self._pending_replay_args['archive_path']
|
| -
|
| -
|
| -
|
| - pending_wpr_mode = self._pending_replay_args['wpr_mode']
|
| - if pending_wpr_mode == wpr_modes.WPR_OFF:
|
| - return
|
| - if (pending_wpr_mode == wpr_modes.WPR_REPLAY and
|
| - not os.path.exists(pending_archive_path)):
|
| - raise ArchiveDoesNotExistError(
|
| - 'Archive path does not exist: %s' % pending_archive_path)
|
| - if browser_backend:
|
| - self._browser_backend = browser_backend
|
| - self.StopReplay() # stop any forwarder too
|
| - wpr_port_pairs = self._browser_backend.wpr_port_pairs
|
| - else:
|
| - # If no browser_backend, then this is an update for an existing browser.
|
| - assert self._browser_backend
|
| - self._StopReplayOnly() # leave existing forwarder in place
|
| - wpr_port_pairs = self._forwarder.port_pairs
|
| - wpr_http_port = wpr_port_pairs.http.local_port
|
| - wpr_https_port = wpr_port_pairs.https.local_port
|
| - wpr_dns_port = (wpr_port_pairs.dns.local_port
|
| - if wpr_port_pairs.dns else None)
|
| -
|
| - archive_path = self._pending_replay_args['archive_path']
|
| - wpr_mode = self._pending_replay_args['wpr_mode']
|
| - netsim = self._pending_replay_args['netsim']
|
| - extra_wpr_args = self._pending_replay_args['extra_wpr_args']
|
| - make_javascript_deterministic = self._pending_replay_args[
|
| - 'make_javascript_deterministic']
|
| -
|
| - if wpr_mode == wpr_modes.WPR_OFF:
|
| - return
|
| - if (wpr_mode == wpr_modes.WPR_REPLAY and
|
| + if (self._wpr_mode == wpr_modes.WPR_REPLAY and
|
| not os.path.exists(archive_path)):
|
| raise ArchiveDoesNotExistError(
|
| 'Archive path does not exist: %s' % archive_path)
|
| + if (self._wpr_server is not None and
|
| + self._archive_path == archive_path and
|
| + self._make_javascript_deterministic == make_javascript_deterministic):
|
| + return # We may reuse the existing replay server.
|
|
|
| - wpr_args = _ReplayCommandLineArgs(
|
| - wpr_mode, netsim, extra_wpr_args, make_javascript_deterministic,
|
| - self._platform_backend.wpr_ca_cert_path)
|
| - self._wpr_server = self._ReplayServer(
|
| - archive_path, self._platform_backend.forwarder_factory.host_ip,
|
| - wpr_http_port, wpr_https_port, wpr_dns_port, wpr_args)
|
| - started_ports = self._wpr_server.StartServer()
|
| -
|
| - if not self._forwarder:
|
| - self._forwarder = self._platform_backend.forwarder_factory.Create(
|
| - _ForwarderPortPairs(started_ports, wpr_port_pairs))
|
| -
|
| - self._active_replay_args = self._pending_replay_args
|
| - self._pending_replay_args = None
|
| -
|
| - def _ReplayServer(
|
| - self, archive_path, host_ip, http_port, https_port, dns_port, wpr_args):
|
| - return webpagereplay.ReplayServer(
|
| - archive_path, host_ip, http_port, https_port, dns_port, wpr_args)
|
| + self._archive_path = archive_path
|
| + self._make_javascript_deterministic = make_javascript_deterministic
|
| + local_ports = self._StartReplayServer()
|
| + self._StartForwarder(local_ports)
|
|
|
| def StopReplay(self):
|
| + """Stop web page replay.
|
| +
|
| + Stops both the replay server and the forwarder if currently active.
|
| + """
|
| if self._forwarder:
|
| self._forwarder.Close()
|
| self._forwarder = None
|
| - self._StopReplayOnly()
|
| -
|
| - def _StopReplayOnly(self):
|
| + self._StopReplayServer()
|
| +
|
| + def _StartReplayServer(self):
|
| + """Start the replay server and return the started local_ports."""
|
| + self._StopReplayServer() # In case it was already running.
|
| + local_ports = self._wpr_port_pairs.local_ports
|
| + self._wpr_server = webpagereplay.ReplayServer(
|
| + self._archive_path,
|
| + self.host_ip,
|
| + local_ports.http,
|
| + local_ports.https,
|
| + local_ports.dns,
|
| + self._ReplayCommandLineArgs())
|
| + return self._wpr_server.StartServer()
|
| +
|
| + def _StopReplayServer(self):
|
| + """Stop the replay server only."""
|
| if self._wpr_server:
|
| self._wpr_server.StopServer()
|
| self._wpr_server = None
|
| - self._active_replay_args = {}
|
|
|
| - @property
|
| - def wpr_http_device_port(self):
|
| - if not self._forwarder or not self._forwarder.port_pairs.http:
|
| - return None
|
| - return self._forwarder.port_pairs.http.remote_port
|
| + def _ReplayCommandLineArgs(self):
|
| + wpr_args = list(self._extra_wpr_args)
|
| + if self._netsim:
|
| + wpr_args.append('--net=%s' % self._netsim)
|
| + if self._wpr_mode == wpr_modes.WPR_APPEND:
|
| + wpr_args.append('--append')
|
| + elif self._wpr_mode == wpr_modes.WPR_RECORD:
|
| + wpr_args.append('--record')
|
| + if not self._make_javascript_deterministic:
|
| + wpr_args.append('--inject_scripts=')
|
| + if self._platform_backend.wpr_ca_cert_path:
|
| + wpr_args.extend([
|
| + '--should_generate_certs',
|
| + '--https_root_ca_cert_path=%s'
|
| + % self._platform_backend.wpr_ca_cert_path])
|
| + return wpr_args
|
| +
|
| + def _StartForwarder(self, local_ports):
|
| + """Start a forwarder from local_ports to the set WPR remote_ports."""
|
| + if self._forwarder is not None:
|
| + if local_ports == self._forwarder.port_pairs.local_ports:
|
| + return # Safe to reuse existing forwarder.
|
| + self._forwarder.Close()
|
| + self._forwarder = self._platform_backend.forwarder_factory.Create(
|
| + forwarders.PortPairs.Zip(local_ports,
|
| + self._wpr_port_pairs.remote_ports))
|
|
|
| - @property
|
| - def wpr_https_device_port(self):
|
| - if not self._forwarder or not self._forwarder.port_pairs.https:
|
| - return None
|
| - return self._forwarder.port_pairs.https.remote_port
|
| -
|
| -
|
| -def _ReplayCommandLineArgs(wpr_mode, netsim, extra_wpr_args,
|
| - make_javascript_deterministic, wpr_ca_cert_path):
|
| - wpr_args = list(extra_wpr_args)
|
| - if netsim:
|
| - wpr_args.append('--net=%s' % netsim)
|
| - if wpr_mode == wpr_modes.WPR_APPEND:
|
| - wpr_args.append('--append')
|
| - elif wpr_mode == wpr_modes.WPR_RECORD:
|
| - wpr_args.append('--record')
|
| - if not make_javascript_deterministic:
|
| - wpr_args.append('--inject_scripts=')
|
| - if wpr_ca_cert_path:
|
| - wpr_args.extend([
|
| - '--should_generate_certs',
|
| - '--https_root_ca_cert_path=%s' % wpr_ca_cert_path,
|
| - ])
|
| - return wpr_args
|
| -
|
| -
|
| -def _ForwarderPortPairs(started_ports, wpr_port_pairs):
|
| - """Return PortPairs with started local ports and requested remote ports.
|
| -
|
| - The local host is where Telemetry is run. The remote is host where
|
| - the target application is run. The local and remote hosts may be
|
| - the same (e.g., testing a desktop browser) or different (e.g., testing
|
| - an android browser).
|
| -
|
| - The remote ports may be zero. In that case, the forwarder determines
|
| - the remote ports.
|
| -
|
| - Args:
|
| - started_ports: a tuple of of integer ports from which to forward:
|
| - (HTTP_PORT, HTTPS_PORT, DNS_PORT) # DNS_PORT may be None
|
| - wpr_port_pairs: a forwarders.PortPairs instance where the remote ports,
|
| - if set, are used.
|
| - Returns:
|
| - a forwarders.PortPairs instance used to create the forwarder.
|
| - """
|
| - local_http_port, local_https_port, local_dns_port = started_ports
|
| - return forwarders.PortPairs(
|
| - forwarders.PortPair(local_http_port, wpr_port_pairs.http.remote_port),
|
| - forwarders.PortPair(local_https_port, wpr_port_pairs.https.remote_port),
|
| - (forwarders.PortPair(local_dns_port, wpr_port_pairs.dns.remote_port)
|
| - if wpr_port_pairs.dns is not None else None))
|
| + def SetReplayArgs(self, archive_path, wpr_mode, netsim, extra_wpr_args,
|
| + make_javascript_deterministic=False):
|
| + """DEPRECATED: New clients should not call this method."""
|
| + if not archive_path:
|
| + return # Same as above. Keep an existing replay server running.
|
| + self._pending_replay_args = dict(
|
| + archive_path=archive_path,
|
| + wpr_mode=wpr_mode,
|
| + netsim=netsim,
|
| + extra_wpr_args=extra_wpr_args,
|
| + make_javascript_deterministic=make_javascript_deterministic)
|
| +
|
| + def UpdateReplay(self, browser_backend=None):
|
| + """DEPRECATED: New clients should not call this method."""
|
| + if not self._pending_replay_args:
|
| + # In some cases (e.g., unit tests), the browser is used without replay.
|
| + return
|
| +
|
| + # TODO(perezju): Move code to figure out WPR port pairts out of browser
|
| + # backends and into the Open method above.
|
| + if browser_backend is None:
|
| + # If no browser_backend, then this is an update for an existing browser.
|
| + assert self.is_open and self._wpr_port_pairs is not None
|
| + wpr_port_pairs = self._wpr_port_pairs
|
| + may_reuse_session = (
|
| + self._pending_replay_args['wpr_mode'] in [wpr_modes.WPR_OFF,
|
| + self._wpr_mode] and
|
| + self._pending_replay_args['netsim'] == self._netsim and
|
| + self._pending_replay_args['extra_wpr_args'] == self._extra_wpr_args)
|
| + else:
|
| + # Use WPR port pairs selected by the browser backend.
|
| + wpr_port_pairs = browser_backend.wpr_port_pairs
|
| + may_reuse_session = False
|
| +
|
| + if not may_reuse_session:
|
| + self.Close() # In case it is already open.
|
| + self.Open(self._pending_replay_args['wpr_mode'],
|
| + self._pending_replay_args['netsim'],
|
| + self._pending_replay_args['extra_wpr_args'])
|
| + # Override port pairs with those chosen by the browser.
|
| + self._wpr_port_pairs = wpr_port_pairs
|
| +
|
| + self.StartReplay(self._pending_replay_args['archive_path'],
|
| + self._pending_replay_args['make_javascript_deterministic'])
|
| +
|
| + # Remember actual port pairs being used after defaults have been resolved.
|
| + # Note: the forwarder would be None if WPR mode is set to off.
|
| + if self._forwarder is not None:
|
| + self._wpr_port_pairs = self._forwarder.port_pairs
|
| + self._pending_replay_args = None
|
|
|