133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#!/usr/bin/env python
233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright 2010 Google Inc. All Rights Reserved.
333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Licensed under the Apache License, Version 2.0 (the "License");
533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# you may not use this file except in compliance with the License.
633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# You may obtain a copy of the License at
733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#      http://www.apache.org/licenses/LICENSE-2.0
933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
1033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Unless required by applicable law or agreed to in writing, software
1133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# distributed under the License is distributed on an "AS IS" BASIS,
1233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# See the License for the specific language governing permissions and
1433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# limitations under the License.
1533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck"""Replays web pages under simulated network conditions.
1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John ReckMust be run as administrator (sudo).
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John ReckTo record web pages:
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  1. Start the program in record mode.
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck     $ sudo ./replay.py --record archive.wpr
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  2. Load the web pages you want to record in a web browser. It is important to
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck     clear browser caches before this so that all subresources are requested
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck     from the network.
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  3. Kill the process to stop recording.
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John ReckTo replay web pages:
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  1. Start the program in replay mode with a previously recorded archive.
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck     $ sudo ./replay.py archive.wpr
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  2. Load recorded pages in a web browser. A 404 will be served for any pages or
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck     resources not in the recorded archive.
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John ReckNetwork simulation examples:
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # 128KByte/s uplink bandwidth, 4Mbps/s downlink bandwidth with 100ms RTT time
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  $ sudo ./replay.py --up 128KByte/s --down 4Mbit/s --delay_ms=100 archive.wpr
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # 1% packet loss rate
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  $ sudo ./replay.py --packet_loss_rate=0.01 archive.wpr
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck"""
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport argparse
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport json
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport logging
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport os
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport socket
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport sys
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport traceback
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport customhandlers
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dnsproxy
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport httparchive
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport httpclient
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport httpproxy
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport net_configs
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport platformsettings
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport rules_parser
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport script_injector
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport servermanager
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport trafficshaper
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckif sys.version < '2.6':
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  print 'Need Python 2.6 or greater.'
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  sys.exit(1)
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef configure_logging(log_level_name, log_file_name=None):
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """Configure logging level and format.
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  Args:
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    log_level_name: 'debug', 'info', 'warning', 'error', or 'critical'.
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    log_file_name: a file name
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if logging.root.handlers:
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logging.critical('A logging method (e.g. "logging.warn(...)")'
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                     ' was called before logging was configured.')
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  log_level = getattr(logging, log_level_name.upper())
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  log_format = (
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    '(%(levelname)s) %(asctime)s %(module)s.%(funcName)s:%(lineno)d  '
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    '%(message)s')
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  logging.basicConfig(level=log_level, format=log_format)
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  logger = logging.getLogger()
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if log_file_name:
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    fh = logging.FileHandler(log_file_name)
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    fh.setLevel(log_level)
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    fh.setFormatter(logging.Formatter(log_format))
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logger.addHandler(fh)
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  system_handler = platformsettings.get_system_logging_handler()
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if system_handler:
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logger.addHandler(system_handler)
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef AddDnsForward(server_manager, host):
9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """Forward DNS traffic."""
9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  server_manager.Append(platformsettings.set_temporary_primary_nameserver, host)
9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef AddDnsProxy(server_manager, options, host, port, real_dns_lookup,
10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                http_archive):
10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  dns_filters = []
10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.dns_private_passthrough:
10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    private_filter = dnsproxy.PrivateIpFilter(real_dns_lookup, http_archive)
10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    dns_filters.append(private_filter)
10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.AppendRecordCallback(private_filter.InitializeArchiveHosts)
10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.AppendReplayCallback(private_filter.InitializeArchiveHosts)
10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.shaping_dns:
10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    delay_filter = dnsproxy.DelayFilter(options.record, **options.shaping_dns)
11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    dns_filters.append(delay_filter)
11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.AppendRecordCallback(delay_filter.SetRecordMode)
11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.AppendReplayCallback(delay_filter.SetReplayMode)
11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  server_manager.Append(dnsproxy.DnsProxyServer, host, port,
11433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                        dns_lookup=dnsproxy.ReplayDnsLookup(host, dns_filters))
11533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef AddWebProxy(server_manager, options, host, real_dns_lookup, http_archive):
11833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.rules_path:
11933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    with open(options.rules_path) as file_obj:
12033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      allowed_imports = [
12133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          name.strip() for name in options.allowed_rule_imports.split(',')]
12233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      rules = rules_parser.Rules(file_obj, allowed_imports)
12333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logging.info('Parsed %s rules:\n%s', options.rules_path, rules)
12433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  else:
12533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    rules = rules_parser.Rules()
12633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  injector = script_injector.GetScriptInjector(options.inject_scripts)
12733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  custom_handlers = customhandlers.CustomHandlers(options, http_archive)
12833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  custom_handlers.add_server_manager_handler(server_manager)
12933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  archive_fetch = httpclient.ControllableHttpArchiveFetch(
13033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      http_archive, real_dns_lookup,
13133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      injector,
13233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      options.diff_unknown_requests, options.record,
13333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      use_closest_match=options.use_closest_match,
13433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      scramble_images=options.scramble_images)
13533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  server_manager.AppendRecordCallback(archive_fetch.SetRecordMode)
13633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  server_manager.AppendReplayCallback(archive_fetch.SetReplayMode)
13733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  allow_generate_304 = not options.record
13833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  server_manager.Append(
13933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      httpproxy.HttpProxyServer,
14033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      archive_fetch, custom_handlers, rules,
14133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      host=host, port=options.port, use_delays=options.use_server_delay,
14233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      allow_generate_304=allow_generate_304,
14333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      **options.shaping_http)
14433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.ssl:
14533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if options.should_generate_certs:
14633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      server_manager.Append(
14733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          httpproxy.HttpsProxyServer, archive_fetch, custom_handlers, rules,
14833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          options.https_root_ca_cert_path, host=host, port=options.ssl_port,
14933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          allow_generate_304=allow_generate_304,
15033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          use_delays=options.use_server_delay, **options.shaping_http)
15133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    else:
15233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      server_manager.Append(
15333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          httpproxy.SingleCertHttpsProxyServer, archive_fetch,
15433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          custom_handlers, rules, options.https_root_ca_cert_path, host=host,
15533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          port=options.ssl_port, use_delays=options.use_server_delay,
15633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          allow_generate_304=allow_generate_304,
15733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          **options.shaping_http)
15833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.http_to_https_port:
15933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.Append(
16033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        httpproxy.HttpToHttpsProxyServer,
16133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        archive_fetch, custom_handlers, rules,
16233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        host=host, port=options.http_to_https_port,
16333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        use_delays=options.use_server_delay,
16433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        allow_generate_304=allow_generate_304,
16533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        **options.shaping_http)
16633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
16733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
16833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef AddTrafficShaper(server_manager, options, host):
16933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.shaping_dummynet:
17033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.AppendTrafficShaper(
17133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        trafficshaper.TrafficShaper, host=host,
17233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        use_loopback=not options.server_mode and host == '127.0.0.1',
17333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        **options.shaping_dummynet)
17433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
17533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
17633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass OptionsWrapper(object):
17733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """Add checks, updates, and methods to option values.
17833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
17933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  Example:
18033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    options, args = arg_parser.parse_args()
18133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    options = OptionsWrapper(options, arg_parser)  # run checks and updates
18233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if options.record and options.HasTrafficShaping():
18333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck       [...]
18433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """
18533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  _TRAFFICSHAPING_OPTIONS = {
18633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      'down', 'up', 'delay_ms', 'packet_loss_rate', 'init_cwnd', 'net'}
18733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  _CONFLICTING_OPTIONS = (
18833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      ('record', ('down', 'up', 'delay_ms', 'packet_loss_rate', 'net',
18933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                  'spdy', 'use_server_delay')),
19033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      ('append', ('down', 'up', 'delay_ms', 'packet_loss_rate', 'net',
19133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                  'use_server_delay')),  # same as --record
19233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      ('net', ('down', 'up', 'delay_ms')),
19333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      ('server', ('server_mode',)),
19433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  )
19533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
19633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def __init__(self, options, parser):
19733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._options = options
19833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._parser = parser
19933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._nondefaults = set([
20033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        action.dest for action in parser._optionals._actions
20133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if getattr(options, action.dest, action.default) is not action.default])
20233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._CheckConflicts()
20333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._CheckValidIp('host')
20433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._CheckFeatureSupport()
20533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._MassageValues()
20633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
20733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def _CheckConflicts(self):
20833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Give an error if mutually exclusive options are used."""
20933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    for option, bad_options in self._CONFLICTING_OPTIONS:
21033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if option in self._nondefaults:
21133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        for bad_option in bad_options:
21233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          if bad_option in self._nondefaults:
21333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            self._parser.error('Option --%s cannot be used with --%s.' %
21433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                (bad_option, option))
21533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
21633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def _CheckValidIp(self, name):
21733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Give an error if option |name| is not a valid IPv4 address."""
21833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    value = getattr(self._options, name)
21933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if value:
22033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      try:
22133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        socket.inet_aton(value)
22233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      except Exception:
22333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self._parser.error('Option --%s must be a valid IPv4 address.' % name)
22433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
22533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def _CheckFeatureSupport(self):
22633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if (self._options.should_generate_certs and
22733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        not platformsettings.HasSniSupport()):
22833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self._parser.error('Option --should_generate_certs requires pyOpenSSL '
22933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                         '0.13 or greater for SNI support.')
23033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
23133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def _ShapingKeywordArgs(self, shaping_key):
23233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Return the shaping keyword args for |shaping_key|.
23333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
23433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    Args:
23533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      shaping_key: one of 'dummynet', 'dns', 'http'.
23633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    Returns:
23733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      {}  # if shaping_key does not apply, or options have default values.
23833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      {k: v, ...}
23933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """
24033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    kwargs = {}
24133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def AddItemIfSet(d, kw_key, opt_key=None):
24233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      opt_key = opt_key or kw_key
24333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if opt_key in self._nondefaults:
24433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        d[kw_key] = getattr(self, opt_key)
24533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if ((self.shaping_type == 'proxy' and shaping_key in ('dns', 'http')) or
24633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.shaping_type == shaping_key):
24733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      AddItemIfSet(kwargs, 'delay_ms')
24833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if shaping_key in ('dummynet', 'http'):
24933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        AddItemIfSet(kwargs, 'down_bandwidth', opt_key='down')
25033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        AddItemIfSet(kwargs, 'up_bandwidth', opt_key='up')
25133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if shaping_key == 'dummynet':
25233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          AddItemIfSet(kwargs, 'packet_loss_rate')
25333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          AddItemIfSet(kwargs, 'init_cwnd')
25433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        elif self.shaping_type != 'none':
25533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          if 'packet_loss_rate' in self._nondefaults:
25633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            logging.warn('Shaping type, %s, ignores --packet_loss_rate=%s',
25733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                         self.shaping_type, self.packet_loss_rate)
25833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          if 'init_cwnd' in self._nondefaults:
25933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            logging.warn('Shaping type, %s, ignores --init_cwnd=%s',
26033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                         self.shaping_type, self.init_cwnd)
26133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return kwargs
26233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
26333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def _MassageValues(self):
26433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Set options that depend on the values of other options."""
26533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if self.append and not self.record:
26633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self._options.record = True
26733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if self.net:
26833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self._options.down, self._options.up, self._options.delay_ms = \
26933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          net_configs.GetNetConfig(self.net)
27033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self._nondefaults.update(['down', 'up', 'delay_ms'])
27133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if not self.ssl:
27233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self._options.https_root_ca_cert_path = None
27333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.shaping_dns = self._ShapingKeywordArgs('dns')
27433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.shaping_http = self._ShapingKeywordArgs('http')
27533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.shaping_dummynet = self._ShapingKeywordArgs('dummynet')
27633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
27733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def __getattr__(self, name):
27833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Make the original option values available."""
27933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return getattr(self._options, name)
28033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
28133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def __repr__(self):
28233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Return a json representation of the original options dictionary."""
28333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return json.dumps(self._options.__dict__)
28433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
28533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def IsRootRequired(self):
28633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Returns True iff the options require whole program root access."""
28733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if self.server:
28833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      return True
28933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
29033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def IsPrivilegedPort(port):
29133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      return port and port < 1024
29233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
29333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if IsPrivilegedPort(self.port) or (self.ssl and
29433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                       IsPrivilegedPort(self.ssl_port)):
29533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      return True
29633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
29733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if self.dns_forwarding:
29833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if IsPrivilegedPort(self.dns_port):
29933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return True
30033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if not self.server_mode and self.host == '127.0.0.1':
30133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return True
30233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
30333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return False
30433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
30533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
30633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef replay(options, replay_filename):
30733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.record and sys.version_info < (2, 7, 9):
30833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    print ('Need Python 2.7.9 or greater for recording mode.\n'
30933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'For instructions on how to upgrade Python on Ubuntu 14.04, see:\n'
31033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'http://mbless.de/blog/2016/01/09/upgrade-to-python-2711-on-ubuntu-1404-lts.html\n')
31133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.admin_check and options.IsRootRequired():
31233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    platformsettings.rerun_as_administrator()
31333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  configure_logging(options.log_level, options.log_file)
31433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  server_manager = servermanager.ServerManager(options.record)
31533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.server:
31633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    AddDnsForward(server_manager, options.server)
31733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  else:
31833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if options.record:
31933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      httparchive.HttpArchive.AssertWritable(replay_filename)
32033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if options.append and os.path.exists(replay_filename):
32133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        http_archive = httparchive.HttpArchive.Load(replay_filename)
32233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        logging.info('Appending to %s (loaded %d existing responses)',
32333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                     replay_filename, len(http_archive))
32433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      else:
32533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        http_archive = httparchive.HttpArchive()
32633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    else:
32733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      http_archive = httparchive.HttpArchive.Load(replay_filename)
32833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      logging.info('Loaded %d responses from %s',
32933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                   len(http_archive), replay_filename)
33033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.AppendRecordCallback(http_archive.clear)
33133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
33233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    ipfw_dns_host = None
33333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if options.dns_forwarding or options.shaping_dummynet:
33433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      # compute the ip/host used for the DNS server and traffic shaping
33533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      ipfw_dns_host = options.host
33633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if not ipfw_dns_host:
33733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        ipfw_dns_host = platformsettings.get_server_ip_address(
33833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            options.server_mode)
33933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
340a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik    real_dns_lookup = dnsproxy.RealDnsLookup(
341a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik        name_servers=[platformsettings.get_original_primary_nameserver()],
342a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik        dns_forwarding=options.dns_forwarding,
343a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik        proxy_host=ipfw_dns_host,
344a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik        proxy_port=options.dns_port)
345a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik    server_manager.AppendRecordCallback(real_dns_lookup.ClearCache)
346a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik
34733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if options.dns_forwarding:
34833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      if not options.server_mode and ipfw_dns_host == '127.0.0.1':
34933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        AddDnsForward(server_manager, ipfw_dns_host)
35033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      AddDnsProxy(server_manager, options, ipfw_dns_host, options.dns_port,
35133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                  real_dns_lookup, http_archive)
35233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if options.ssl and options.https_root_ca_cert_path is None:
35333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      options.https_root_ca_cert_path = os.path.join(os.path.dirname(__file__),
35433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                                     'wpr_cert.pem')
35533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    http_proxy_address = options.host
35633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if not http_proxy_address:
35733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      http_proxy_address = platformsettings.get_httpproxy_ip_address(
35833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          options.server_mode)
35933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    AddWebProxy(server_manager, options, http_proxy_address, real_dns_lookup,
36033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                http_archive)
36133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    AddTrafficShaper(server_manager, options, ipfw_dns_host)
36233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
36333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  exit_status = 0
36433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  try:
36533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    server_manager.Run()
36633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  except KeyboardInterrupt:
36733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logging.info('Shutting down.')
36833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  except (dnsproxy.DnsProxyException,
36933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          trafficshaper.TrafficShaperException,
37033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          platformsettings.NotAdministratorError,
37133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          platformsettings.DnsUpdateError) as e:
37233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logging.critical('%s: %s', e.__class__.__name__, e)
37333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    exit_status = 1
37433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  except Exception:
37533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logging.critical(traceback.format_exc())
37633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    exit_status = 2
37733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
37833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.record:
37933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    http_archive.Persist(replay_filename)
38033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    logging.info('Saved %d responses to %s', len(http_archive), replay_filename)
38133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  return exit_status
38233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
38333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
38433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef GetParser():
38533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  arg_parser = argparse.ArgumentParser(
38633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      usage='%(prog)s [options] replay_file',
38733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      description=__doc__,
38833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      formatter_class=argparse.RawDescriptionHelpFormatter,
38933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      epilog='http://code.google.com/p/web-page-replay/')
39033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
39133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  arg_parser.add_argument('replay_filename', type=str, help='Replay file',
39233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                          nargs='?')
39333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
39433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  arg_parser.add_argument('-r', '--record', default=False,
39533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_true',
39633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Download real responses and record them to replay_file')
39733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  arg_parser.add_argument('--append', default=False,
39833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_true',
39933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Append responses to replay_file.')
40033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  arg_parser.add_argument('-l', '--log_level', default='debug',
40133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
40233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
40333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      choices=('debug', 'info', 'warning', 'error', 'critical'),
40433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Minimum verbosity level to log')
40533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  arg_parser.add_argument('-f', '--log_file', default=None,
40633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
40733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
40833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Log file to use in addition to writting logs to stderr.')
40933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
41033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group = arg_parser.add_argument_group(
41133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      title='Network Simulation Options',
41233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      description=('These options configure the network simulation in '
41333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                   'replay mode'))
41433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group.add_argument('-u', '--up', default='0',
41533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
41633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
41733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Upload Bandwidth in [K|M]{bit/s|Byte/s}. Zero means unlimited.')
41833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group.add_argument('-d', '--down', default='0',
41933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
42033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
42133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Download Bandwidth in [K|M]{bit/s|Byte/s}. Zero means unlimited.')
42233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group.add_argument('-m', '--delay_ms', default='0',
42333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
42433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
42533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Propagation delay (latency) in milliseconds. Zero means no delay.')
42633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group.add_argument('-p', '--packet_loss_rate', default='0',
42733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
42833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
42933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Packet loss rate in range [0..1]. Zero means no loss.')
43033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group.add_argument('-w', '--init_cwnd', default='0',
43133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
43233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
43333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Set initial cwnd (linux only, requires kernel patch)')
43433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group.add_argument('--net', default=None,
43533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
43633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
43733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      choices=net_configs.NET_CONFIG_NAMES,
43833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Select a set of network options: %s.' % ', '.join(
43933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck          net_configs.NET_CONFIG_NAMES))
44033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  network_group.add_argument('--shaping_type', default='dummynet',
44133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
44233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      choices=('dummynet', 'proxy'),
44333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='When shaping is configured (i.e. --up, --down, etc.) decides '
44433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'whether to use |dummynet| (default), or |proxy| servers.')
44533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
44633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group = arg_parser.add_argument_group(
44733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      title='Replay Harness Options',
44833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      description=('These advanced options configure various aspects '
44933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                   'of the replay harness'))
45033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-S', '--server', default=None,
45133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
45233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
45333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='IP address of host running "replay.py --server_mode". '
45433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'This only changes the primary DNS nameserver to use the given IP.')
45533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-M', '--server_mode', default=False,
45633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_true',
45733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Run replay DNS & http proxies, and trafficshaping on --port '
45833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'without changing the primary DNS nameserver. '
45933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'Other hosts may connect to this using "replay.py --server" '
46033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'or by pointing their DNS to this server.')
46133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-i', '--inject_scripts', default='deterministic.js',
46233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
46333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='inject_scripts',
46433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='A comma separated list of JavaScript sources to inject in all '
46533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'pages. By default a script is injected that eliminates sources '
46633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'of entropy such as Date() and Math.random() deterministic. '
46733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'CAUTION: Without deterministic.js, many pages will not replay.')
46833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-D', '--no-diff_unknown_requests', default=True,
46933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_false',
47033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='diff_unknown_requests',
47133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='During replay, do not show a diff of unknown requests against '
47233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'their nearest match in the archive.')
47333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-C', '--use_closest_match', default=False,
47433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_true',
47533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='use_closest_match',
47633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='During replay, if a request is not found, serve the closest match'
47733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'in the archive instead of giving a 404.')
47833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-U', '--use_server_delay', default=False,
47933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_true',
48033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='use_server_delay',
48133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='During replay, simulate server delay by delaying response time to'
48233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'requests.')
48333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-I', '--screenshot_dir', default=None,
48433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
48533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
48633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Save PNG images of the loaded page in the given directory.')
48733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-P', '--no-dns_private_passthrough', default=True,
48833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_false',
48933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='dns_private_passthrough',
49033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Don\'t forward DNS requests that resolve to private network '
49133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'addresses. CAUTION: With this option important services like '
49233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'Kerberos will resolve to the HTTP proxy address.')
49333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-x', '--no-dns_forwarding', default=True,
49433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_false',
49533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='dns_forwarding',
49633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Don\'t forward DNS requests to the local replay server. '
49733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'CAUTION: With this option an external mechanism must be used to '
49833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'forward traffic to the replay server.')
49933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--host', default=None,
50033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
50133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
50233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='The IP address to bind all servers to. Defaults to 0.0.0.0 or '
50333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           '127.0.0.1, depending on --server_mode and platform.')
50433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-o', '--port', default=80,
50533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
50633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=int,
50733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Port number to listen on.')
50833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--ssl_port', default=443,
50933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
51033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=int,
51133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='SSL port number to listen on.')
51233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--http_to_https_port', default=None,
51333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
51433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=int,
51533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Port on which WPR will listen for HTTP requests that it will send '
51633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           'along as HTTPS requests.')
51733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--dns_port', default=53,
51833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
51933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=int,
52033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='DNS port number to listen on.')
52133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('-c', '--https_root_ca_cert_path', default=None,
52233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
52333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      type=str,
52433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Certificate file to use with SSL (gets auto-generated if needed).')
52533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--no-ssl', default=True,
52633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_false',
52733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='ssl',
52833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Do not setup an SSL proxy.')
52933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--should_generate_certs', default=False,
53033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_true',
53133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Use OpenSSL to generate certificate files for requested hosts.')
53233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--no-admin-check', default=True,
53333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_false',
53433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='admin_check',
53533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Do not check if administrator access is needed.')
53633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--scramble_images', default=False,
53733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store_true',
53833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      dest='scramble_images',
53933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Scramble image responses.')
54033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--rules_path', default=None,
54133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
54233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='Path of file containing Python rules.')
54333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  harness_group.add_argument('--allowed_rule_imports', default='rules',
54433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      action='store',
54533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      help='A comma-separate list of allowed rule imports, or \'*\' to allow'
54633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           ' all packages.  Defaults to %(default)s.')
54733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  return arg_parser
54833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
54933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
55033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef main():
55133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  arg_parser = GetParser()
55233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  options = arg_parser.parse_args()
55333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  options = OptionsWrapper(options, arg_parser)
55433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
55533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if options.server:
55633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    options.replay_filename = None
55733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  elif options.replay_filename is None:
55833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    arg_parser.error('Must specify a replay_file')
55933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  return replay(options, options.replay_filename)
56033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
56133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
56233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckif __name__ == '__main__':
56333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  sys.exit(main())
564