// 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.

#include "components/network_session_configurator/browser/network_session_configurator.h"

#include <limits>
#include <map>
#include <unordered_set>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "components/network_session_configurator/common/network_features.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/variations/variations_associated_data.h"
#include "net/base/host_mapping_rules.h"
#include "net/http/http_stream_factory.h"
#include "net/quic/platform/impl/quic_flags_impl.h"
#include "net/quic/quic_context.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/third_party/quiche/src/quic/core/quic_packets.h"
#include "net/third_party/quiche/src/quic/core/quic_tag.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"

#if defined(OS_MAC)
#include "base/mac/mac_util.h"
#endif

namespace {

// Map from name to value for all parameters associate with a field trial.
using VariationParameters = std::map<std::string, std::string>;

const char kQuicFieldTrialName[] = "QUIC";

const char kHttp2FieldTrialName[] = "HTTP2";

// Gets the value of the specified command line switch, as an integer. If unable
// to convert it to an int (It's not an int, or the switch is not present)
// returns 0.
int GetSwitchValueAsInt(const base::CommandLine& command_line,
                        const std::string& switch_name) {
  int value;
  if (!base::StringToInt(command_line.GetSwitchValueASCII(switch_name),
                         &value)) {
    return 0;
  }
  return value;
}

// Returns the value associated with |key| in |params| or "" if the
// key is not present in the map.
const std::string& GetVariationParam(
    const std::map<std::string, std::string>& params,
    const std::string& key) {
  auto it = params.find(key);
  if (it == params.end())
    return base::EmptyString();

  return it->second;
}

spdy::SettingsMap GetHttp2Settings(
    const VariationParameters& http2_trial_params) {
  spdy::SettingsMap http2_settings;

  const std::string settings_string =
      GetVariationParam(http2_trial_params, "http2_settings");

  base::StringPairs key_value_pairs;
  if (!base::SplitStringIntoKeyValuePairs(settings_string, ':', ',',
                                          &key_value_pairs)) {
    return http2_settings;
  }

  for (auto key_value : key_value_pairs) {
    uint32_t key;
    if (!base::StringToUint(key_value.first, &key))
      continue;
    uint32_t value;
    if (!base::StringToUint(key_value.second, &value))
      continue;
    http2_settings[static_cast<spdy::SpdyKnownSettingsId>(key)] = value;
  }

  return http2_settings;
}

bool ConfigureWebsocketOverHttp2(
    const base::CommandLine& command_line,
    const VariationParameters& http2_trial_params) {
  if (command_line.HasSwitch(switches::kEnableWebsocketOverHttp2))
    return true;

  const std::string websocket_value =
      GetVariationParam(http2_trial_params, "websocket_over_http2");
  return websocket_value == "true";
}

int ConfigureSpdySessionMaxQueuedCappedFrames(
    const base::CommandLine& /*command_line*/,
    const VariationParameters& http2_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(http2_trial_params,
                            "spdy_session_max_queued_capped_frames"),
          &value)) {
    return value;
  }
  return net::kSpdySessionMaxQueuedCappedFrames;
}

void ConfigureHttp2Params(const base::CommandLine& command_line,
                          base::StringPiece http2_trial_group,
                          const VariationParameters& http2_trial_params,
                          net::HttpNetworkSession::Params* params) {
  if (GetVariationParam(http2_trial_params, "http2_enabled") == "false") {
    params->enable_http2 = false;
    return;
  }

  // After parsing initial settings, optionally add a setting with reserved
  // identifier to "grease" settings, see
  // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
  params->http2_settings = GetHttp2Settings(http2_trial_params);
  if (command_line.HasSwitch(switches::kHttp2GreaseSettings) ||
      GetVariationParam(http2_trial_params, "http2_grease_settings") ==
          "true") {
    spdy::SpdySettingsId id = 0x0a0a + 0x1000 * base::RandGenerator(0xf + 1) +
                              0x0010 * base::RandGenerator(0xf + 1);
    uint32_t value = base::RandGenerator(
        static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1);
    params->http2_settings.insert(std::make_pair(id, value));
  }

  // Optionally define a frame of reserved type to "grease" frame types, see
  // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
  if (command_line.HasSwitch(switches::kHttp2GreaseFrameType) ||
      GetVariationParam(http2_trial_params, "http2_grease_frame_type") ==
          "true") {
    const uint8_t type = 0x0b + 0x1f * base::RandGenerator(8);

    uint8_t flags;
    base::RandBytes(&flags, /* output_length = */ sizeof(flags));

    const size_t length = base::RandGenerator(7);
    // RandBytesAsString() does not support zero length.
    const std::string payload =
        (length > 0) ? base::RandBytesAsString(length) : std::string();

    params->greased_http2_frame =
        base::Optional<net::SpdySessionPool::GreasedHttp2Frame>(
            {type, flags, payload});
  }

  if (command_line.HasSwitch(switches::kHttp2EndStreamWithDataFrame) ||
      GetVariationParam(http2_trial_params,
                        "http2_end_stream_with_data_frame") == "true") {
    params->http2_end_stream_with_data_frame = true;
  }

  params->enable_websocket_over_http2 =
      ConfigureWebsocketOverHttp2(command_line, http2_trial_params);

  params->spdy_session_max_queued_capped_frames =
      ConfigureSpdySessionMaxQueuedCappedFrames(command_line,
                                                http2_trial_params);
}

bool ShouldDisableQuic(base::StringPiece quic_trial_group,
                       const VariationParameters& quic_trial_params,
                       bool is_quic_force_disabled) {
  if (is_quic_force_disabled)
    return true;

  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "enable_quic"), "false");
}

bool ShouldEnableQuicProxiesForHttpsUrls(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params,
                        "enable_quic_proxies_for_https_urls"),
      "true");
}

bool ShouldRetryWithoutAltSvcOnQuicErrors(
    const VariationParameters& quic_trial_params) {
  return !base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params,
                        "retry_without_alt_svc_on_quic_errors"),
      "false");
}

quic::QuicTagVector GetQuicConnectionOptions(
    const VariationParameters& quic_trial_params) {
  auto it = quic_trial_params.find("connection_options");
  if (it == quic_trial_params.end()) {
    return quic::QuicTagVector();
  }

  return quic::ParseQuicTagVector(it->second);
}

quic::QuicTagVector GetQuicClientConnectionOptions(
    const VariationParameters& quic_trial_params) {
  auto it = quic_trial_params.find("client_connection_options");
  if (it == quic_trial_params.end()) {
    return quic::QuicTagVector();
  }

  return quic::ParseQuicTagVector(it->second);
}

bool ShouldQuicCloseSessionsOnIpChange(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "close_sessions_on_ip_change"),
      "true");
}

bool ShouldQuicGoAwaySessionsOnIpChange(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "goaway_sessions_on_ip_change"),
      "true");
}

int GetQuicIdleConnectionTimeoutSeconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(GetVariationParam(quic_trial_params,
                                          "idle_connection_timeout_seconds"),
                        &value)) {
    return value;
  }
  return 0;
}

int GetQuicReducedPingTimeoutSeconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(quic_trial_params, "reduced_ping_timeout_seconds"),
          &value)) {
    return value;
  }
  return 0;
}

int GetQuicMaxTimeBeforeCryptoHandshakeSeconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(quic_trial_params,
                            "max_time_before_crypto_handshake_seconds"),
          &value)) {
    return value;
  }
  return 0;
}

int GetQuicMaxIdleTimeBeforeCryptoHandshakeSeconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(quic_trial_params,
                            "max_idle_time_before_crypto_handshake_seconds"),
          &value)) {
    return value;
  }
  return 0;
}

bool ShouldQuicEstimateInitialRtt(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "estimate_initial_rtt"), "true");
}

bool ShouldQuicHeadersIncludeH2StreamDependencies(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params,
                        "headers_include_h2_stream_dependency"),
      "true");
}

bool ShouldQuicMigrateSessionsOnNetworkChangeV2(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params,
                        "migrate_sessions_on_network_change_v2"),
      "true");
}

bool ShouldQuicMigrateSessionsEarlyV2(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "migrate_sessions_early_v2"),
      "true");
}

bool ShouldQuicAllowPortMigration(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "allow_port_migration"), "true");
}

bool ShouldQuicRetryOnAlternateNetworkBeforeHandshake(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params,
                        "retry_on_alternate_network_before_handshake"),
      "true");
}

bool ShouldQuicGoawayOnPathDegrading(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "go_away_on_path_degrading"),
      "true");
}

int GetQuicMaxTimeOnNonDefaultNetworkSeconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(quic_trial_params,
                            "max_time_on_non_default_network_seconds"),
          &value)) {
    return value;
  }
  return 0;
}

bool ShouldQuicMigrateIdleSessions(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "migrate_idle_sessions"), "true");
}

bool ShouldQuicDisableTlsZeroRtt(const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "disable_tls_zero_rtt"), "true");
}

bool ShouldQuicDisableGQuicZeroRtt(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "disable_gquic_zero_rtt"), "true");
}

int GetQuicRetransmittableOnWireTimeoutMilliseconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(quic_trial_params,
                            "retransmittable_on_wire_timeout_milliseconds"),
          &value)) {
    return value;
  }
  return 0;
}

int GetQuicIdleSessionMigrationPeriodSeconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(quic_trial_params,
                            "idle_session_migration_period_seconds"),
          &value)) {
    return value;
  }
  return 0;
}

int GetQuicMaxNumMigrationsToNonDefaultNetworkOnWriteError(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(
              quic_trial_params,
              "max_migrations_to_non_default_network_on_write_error"),
          &value)) {
    return value;
  }
  return 0;
}

int GetQuicMaxNumMigrationsToNonDefaultNetworkOnPathDegrading(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(
              quic_trial_params,
              "max_migrations_to_non_default_network_on_path_degrading"),
          &value)) {
    return value;
  }
  return 0;
}

int GetQuicInitialRttForHandshakeMilliseconds(
    const VariationParameters& quic_trial_params) {
  int value;
  if (base::StringToInt(
          GetVariationParam(quic_trial_params,
                            "initial_rtt_for_handshake_milliseconds"),
          &value)) {
    return value;
  }
  return 0;
}

base::flat_set<std::string> GetQuicHostAllowlist(
    const VariationParameters& quic_trial_params) {
  std::string host_allowlist =
      GetVariationParam(quic_trial_params, "host_whitelist");
  std::vector<std::string> host_vector = base::SplitString(
      host_allowlist, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
  return base::flat_set<std::string>(std::move(host_vector));
}

void SetQuicFlags(const VariationParameters& quic_trial_params) {
  std::string flags_list =
      GetVariationParam(quic_trial_params, "set_quic_flags");
  for (const auto& flag : base::SplitString(
           flags_list, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
    std::vector<std::string> tokens = base::SplitString(
        flag, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
    if (tokens.size() != 2)
      continue;
    SetQuicFlagByName(tokens[0], tokens[1]);
  }
}

size_t GetQuicMaxPacketLength(const VariationParameters& quic_trial_params) {
  unsigned value;
  if (base::StringToUint(
          GetVariationParam(quic_trial_params, "max_packet_length"), &value)) {
    return value;
  }
  return 0;
}

quic::ParsedQuicVersionVector GetQuicVersions(
    const VariationParameters& quic_trial_params) {
  quic::ParsedQuicVersionVector trial_versions =
      quic::ParseQuicVersionVectorString(
          GetVariationParam(quic_trial_params, "quic_version"));
  const bool obsolete_versions_allowed = base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "obsolete_versions_allowed"),
      "true");
  if (!obsolete_versions_allowed) {
    quic::ParsedQuicVersionVector filtered_versions;
    quic::ParsedQuicVersionVector obsolete_versions =
        net::ObsoleteQuicVersions();
    for (const quic::ParsedQuicVersion& version : trial_versions) {
      if (std::find(obsolete_versions.begin(), obsolete_versions.end(),
                    version) == obsolete_versions.end()) {
        filtered_versions.push_back(version);
      }
    }
    trial_versions = filtered_versions;
  }
  return trial_versions;
}

bool ShouldEnableServerPushCancelation(
    const VariationParameters& quic_trial_params) {
  return base::LowerCaseEqualsASCII(
      GetVariationParam(quic_trial_params, "enable_server_push_cancellation"),
      "true");
}

void ConfigureQuicParams(base::StringPiece quic_trial_group,
                         const VariationParameters& quic_trial_params,
                         bool is_quic_force_disabled,
                         const std::string& quic_user_agent_id,
                         net::HttpNetworkSession::Params* params,
                         net::QuicParams* quic_params) {
  if (ShouldDisableQuic(quic_trial_group, quic_trial_params,
                        is_quic_force_disabled)) {
    params->enable_quic = false;
  }

  params->enable_server_push_cancellation =
      ShouldEnableServerPushCancelation(quic_trial_params);

  quic_params->retry_without_alt_svc_on_quic_errors =
      ShouldRetryWithoutAltSvcOnQuicErrors(quic_trial_params);

  if (params->enable_quic) {
    params->enable_quic_proxies_for_https_urls =
        ShouldEnableQuicProxiesForHttpsUrls(quic_trial_params);
    quic_params->connection_options =
        GetQuicConnectionOptions(quic_trial_params);
    quic_params->client_connection_options =
        GetQuicClientConnectionOptions(quic_trial_params);
    quic_params->close_sessions_on_ip_change =
        ShouldQuicCloseSessionsOnIpChange(quic_trial_params);
    quic_params->goaway_sessions_on_ip_change =
        ShouldQuicGoAwaySessionsOnIpChange(quic_trial_params);
    int idle_connection_timeout_seconds =
        GetQuicIdleConnectionTimeoutSeconds(quic_trial_params);
    if (idle_connection_timeout_seconds != 0) {
      quic_params->idle_connection_timeout =
          base::TimeDelta::FromSeconds(idle_connection_timeout_seconds);
    }
    int reduced_ping_timeout_seconds =
        GetQuicReducedPingTimeoutSeconds(quic_trial_params);
    if (reduced_ping_timeout_seconds > 0 &&
        reduced_ping_timeout_seconds < quic::kPingTimeoutSecs) {
      quic_params->reduced_ping_timeout =
          base::TimeDelta::FromSeconds(reduced_ping_timeout_seconds);
    }
    int max_time_before_crypto_handshake_seconds =
        GetQuicMaxTimeBeforeCryptoHandshakeSeconds(quic_trial_params);
    if (max_time_before_crypto_handshake_seconds > 0) {
      quic_params->max_time_before_crypto_handshake =
          base::TimeDelta::FromSeconds(
              max_time_before_crypto_handshake_seconds);
    }
    int max_idle_time_before_crypto_handshake_seconds =
        GetQuicMaxIdleTimeBeforeCryptoHandshakeSeconds(quic_trial_params);
    if (max_idle_time_before_crypto_handshake_seconds > 0) {
      quic_params->max_idle_time_before_crypto_handshake =
          base::TimeDelta::FromSeconds(
              max_idle_time_before_crypto_handshake_seconds);
    }
    quic_params->estimate_initial_rtt =
        ShouldQuicEstimateInitialRtt(quic_trial_params);
    quic_params->headers_include_h2_stream_dependency =
        ShouldQuicHeadersIncludeH2StreamDependencies(quic_trial_params);
    quic_params->migrate_sessions_on_network_change_v2 =
        ShouldQuicMigrateSessionsOnNetworkChangeV2(quic_trial_params);
    quic_params->migrate_sessions_early_v2 =
        ShouldQuicMigrateSessionsEarlyV2(quic_trial_params);
    quic_params->allow_port_migration =
        ShouldQuicAllowPortMigration(quic_trial_params);
    quic_params->retry_on_alternate_network_before_handshake =
        ShouldQuicRetryOnAlternateNetworkBeforeHandshake(quic_trial_params);
    quic_params->go_away_on_path_degrading =
        ShouldQuicGoawayOnPathDegrading(quic_trial_params);
    int initial_rtt_for_handshake_milliseconds =
        GetQuicInitialRttForHandshakeMilliseconds(quic_trial_params);
    if (initial_rtt_for_handshake_milliseconds > 0) {
      quic_params->initial_rtt_for_handshake =
          base::TimeDelta::FromMilliseconds(
              initial_rtt_for_handshake_milliseconds);
    }

    quic_params->disable_tls_zero_rtt =
        ShouldQuicDisableTlsZeroRtt(quic_trial_params);

    quic_params->disable_gquic_zero_rtt =
        ShouldQuicDisableGQuicZeroRtt(quic_trial_params);

    int retransmittable_on_wire_timeout_milliseconds =
        GetQuicRetransmittableOnWireTimeoutMilliseconds(quic_trial_params);
    if (retransmittable_on_wire_timeout_milliseconds > 0) {
      quic_params->retransmittable_on_wire_timeout =
          base::TimeDelta::FromMilliseconds(
              retransmittable_on_wire_timeout_milliseconds);
    }
    quic_params->migrate_idle_sessions =
        ShouldQuicMigrateIdleSessions(quic_trial_params);
    int idle_session_migration_period_seconds =
        GetQuicIdleSessionMigrationPeriodSeconds(quic_trial_params);
    if (idle_session_migration_period_seconds > 0) {
      quic_params->idle_session_migration_period =
          base::TimeDelta::FromSeconds(idle_session_migration_period_seconds);
    }
    int max_time_on_non_default_network_seconds =
        GetQuicMaxTimeOnNonDefaultNetworkSeconds(quic_trial_params);
    if (max_time_on_non_default_network_seconds > 0) {
      quic_params->max_time_on_non_default_network =
          base::TimeDelta::FromSeconds(max_time_on_non_default_network_seconds);
    }
    int max_migrations_to_non_default_network_on_write_error =
        GetQuicMaxNumMigrationsToNonDefaultNetworkOnWriteError(
            quic_trial_params);
    if (max_migrations_to_non_default_network_on_write_error > 0) {
      quic_params->max_migrations_to_non_default_network_on_write_error =
          max_migrations_to_non_default_network_on_write_error;
    }
    int max_migrations_to_non_default_network_on_path_degrading =
        GetQuicMaxNumMigrationsToNonDefaultNetworkOnPathDegrading(
            quic_trial_params);
    if (max_migrations_to_non_default_network_on_path_degrading > 0) {
      quic_params->max_migrations_to_non_default_network_on_path_degrading =
          max_migrations_to_non_default_network_on_path_degrading;
    }
    params->quic_host_allowlist = GetQuicHostAllowlist(quic_trial_params);

    SetQuicFlags(quic_trial_params);
  }

  size_t max_packet_length = GetQuicMaxPacketLength(quic_trial_params);
  if (max_packet_length != 0) {
    quic_params->max_packet_length = max_packet_length;
  }

  quic_params->user_agent_id = quic_user_agent_id;

  quic::ParsedQuicVersionVector supported_versions =
      GetQuicVersions(quic_trial_params);
  if (!supported_versions.empty())
    quic_params->supported_versions = supported_versions;
}

}  // anonymous namespace

namespace network_session_configurator {

void ParseCommandLineAndFieldTrials(const base::CommandLine& command_line,
                                    bool is_quic_force_disabled,
                                    const std::string& quic_user_agent_id,
                                    net::HttpNetworkSession::Params* params,
                                    net::QuicParams* quic_params) {
  is_quic_force_disabled |= command_line.HasSwitch(switches::kDisableQuic);

  std::string quic_trial_group =
      base::FieldTrialList::FindFullName(kQuicFieldTrialName);
  VariationParameters quic_trial_params;
  if (!variations::GetVariationParams(kQuicFieldTrialName, &quic_trial_params))
    quic_trial_params.clear();
  ConfigureQuicParams(quic_trial_group, quic_trial_params,
                      is_quic_force_disabled, quic_user_agent_id, params,
                      quic_params);

  std::string http2_trial_group =
      base::FieldTrialList::FindFullName(kHttp2FieldTrialName);
  VariationParameters http2_trial_params;
  if (!variations::GetVariationParams(kHttp2FieldTrialName,
                                      &http2_trial_params))
    http2_trial_params.clear();
  ConfigureHttp2Params(command_line, http2_trial_group, http2_trial_params,
                       params);

  // Command line flags override field trials.
  if (command_line.HasSwitch(switches::kDisableHttp2))
    params->enable_http2 = false;

  if (params->enable_quic) {
    if (command_line.HasSwitch(switches::kQuicConnectionOptions)) {
      quic_params->connection_options = quic::ParseQuicTagVector(
          command_line.GetSwitchValueASCII(switches::kQuicConnectionOptions));
    }
    if (command_line.HasSwitch(switches::kQuicClientConnectionOptions)) {
      quic_params->client_connection_options =
          quic::ParseQuicTagVector(command_line.GetSwitchValueASCII(
              switches::kQuicClientConnectionOptions));
    }

    if (command_line.HasSwitch(switches::kQuicMaxPacketLength)) {
      unsigned value;
      if (base::StringToUint(
              command_line.GetSwitchValueASCII(switches::kQuicMaxPacketLength),
              &value)) {
        quic_params->max_packet_length = value;
      }
    }

    if (command_line.HasSwitch(switches::kQuicVersion)) {
      quic::ParsedQuicVersionVector supported_versions =
          quic::ParseQuicVersionVectorString(
              command_line.GetSwitchValueASCII(switches::kQuicVersion));
      if (!supported_versions.empty())
        quic_params->supported_versions = supported_versions;
    }

    if (command_line.HasSwitch(switches::kOriginToForceQuicOn)) {
      std::string origins =
          command_line.GetSwitchValueASCII(switches::kOriginToForceQuicOn);
      for (const std::string& host_port : base::SplitString(
               origins, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
        if (host_port == "*")
          quic_params->origins_to_force_quic_on.insert(net::HostPortPair());
        net::HostPortPair quic_origin =
            net::HostPortPair::FromString(host_port);
        if (!quic_origin.IsEmpty())
          quic_params->origins_to_force_quic_on.insert(quic_origin);
      }
    }
  }

  // Parameters only controlled by command line.
  if (command_line.HasSwitch(switches::kEnableUserAlternateProtocolPorts)) {
    params->enable_user_alternate_protocol_ports = true;
  }
  if (command_line.HasSwitch(switches::kIgnoreCertificateErrors)) {
    params->ignore_certificate_errors = true;
  }
  UMA_HISTOGRAM_BOOLEAN(
      "Net.Certificate.IgnoreErrors",
      command_line.HasSwitch(switches::kIgnoreCertificateErrors));
  if (command_line.HasSwitch(switches::kTestingFixedHttpPort)) {
    params->testing_fixed_http_port =
        GetSwitchValueAsInt(command_line, switches::kTestingFixedHttpPort);
  }
  if (command_line.HasSwitch(switches::kTestingFixedHttpsPort)) {
    params->testing_fixed_https_port =
        GetSwitchValueAsInt(command_line, switches::kTestingFixedHttpsPort);
  }

  if (command_line.HasSwitch(switches::kHostRules)) {
    params->host_mapping_rules.SetRulesFromString(
        command_line.GetSwitchValueASCII(switches::kHostRules));
  }
}

net::URLRequestContextBuilder::HttpCacheParams::Type ChooseCacheType() {
#if !defined(OS_ANDROID)
  const std::string experiment_name =
      base::FieldTrialList::FindFullName("SimpleCacheTrial");
  if (base::StartsWith(experiment_name, "Disable",
                       base::CompareCase::INSENSITIVE_ASCII)) {
    return net::URLRequestContextBuilder::HttpCacheParams::DISK_BLOCKFILE;
  }

  // Blockfile breaks on OSX 10.14 (see https://crbug.com/899874); so use
  // SimpleCache even when we don't enable it via experiment, as long as we
  // don't force it off (not used at this time). This unfortunately
  // muddles the experiment data, but as this was written to be considered for
  // backport, having it behave differently than in stable would be a bigger
  // problem.
#if defined(OS_MAC)
  if (base::mac::IsAtLeastOS10_14())
    return net::URLRequestContextBuilder::HttpCacheParams::DISK_SIMPLE;
#endif  // defined(OS_MAC)

  if (base::StartsWith(experiment_name, "ExperimentYes",
                       base::CompareCase::INSENSITIVE_ASCII)) {
    return net::URLRequestContextBuilder::HttpCacheParams::DISK_SIMPLE;
  }
#endif  // #if !defined(OS_ANDROID)

#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
  return net::URLRequestContextBuilder::HttpCacheParams::DISK_SIMPLE;
#else
  return net::URLRequestContextBuilder::HttpCacheParams::DISK_BLOCKFILE;
#endif
}

}  // namespace network_session_configurator
