diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc index 99b582d3d70a..75a27b117a44 100644 --- a/cobalt/browser/browser_module.cc +++ b/cobalt/browser/browser_module.cc @@ -448,6 +448,12 @@ BrowserModule::~BrowserModule() { } void BrowserModule::Navigate(const GURL& url_reference) { + if (network_module_) { + // If protocolfilter setting was updated, the setting does not take effect + // until the next page load. + network_module_->SetProtocolFilterFromPersistentSettings(); + } + // The argument is sometimes |pending_navigate_url_|, and Navigate can modify // |pending_navigate_url_|, so we want to keep a copy of the argument to // preserve its original value. diff --git a/cobalt/h5vcc/h5vcc_settings.cc b/cobalt/h5vcc/h5vcc_settings.cc index 3c6062001737..382febda7d99 100644 --- a/cobalt/h5vcc/h5vcc_settings.cc +++ b/cobalt/h5vcc/h5vcc_settings.cc @@ -118,6 +118,32 @@ bool H5vccSettings::Set(const std::string& name, SetValueType value) const { } } + if (name.compare(network::kProtocolFilterKey) == 0 && + value.IsType() && + value.AsType().size() < 16384) { + std::string raw_json = value.AsType(); + base::Value old_config_json; + persistent_settings_->Get(network::kProtocolFilterKey, &old_config_json); + + if (raw_json.empty() && (!old_config_json.is_string() || + !old_config_json.GetString().empty())) { + persistent_settings_->Set(network::kProtocolFilterKey, base::Value()); + network_module_->SetProtocolFilterUpdatePending(); + return true; + } + + absl::optional old_config = + base::JSONReader::Read(old_config_json.GetString()); + absl::optional new_config = base::JSONReader::Read(raw_json); + if (!new_config) return false; + if (old_config && *old_config == *new_config) return false; + + persistent_settings_->Set(network::kProtocolFilterKey, + base::Value(raw_json)); + network_module_->SetProtocolFilterUpdatePending(); + return true; + } + if (name.compare("cpu_usage_tracker_intervals") == 0 && value.IsType() && value.AsType().size() < 512) { absl::optional config = @@ -193,6 +219,13 @@ std::string H5vccSettings::GetPersistentSettingAsString( absl::optional json = base::WriteJson(value); return json.value_or(std::string()); } + + if (key.compare(network::kProtocolFilterKey) == 0) { + base::Value value; + persistent_settings_->Get(network::kProtocolFilterKey, &value); + if (!value.is_string()) return std::string(); + return value.GetString(); + } return std::string(); } diff --git a/cobalt/network/network_module.cc b/cobalt/network/network_module.cc index 8b2ec4558009..8954d43c3fe1 100644 --- a/cobalt/network/network_module.cc +++ b/cobalt/network/network_module.cc @@ -18,6 +18,7 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/json/json_reader.h" #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" @@ -160,6 +161,74 @@ void NetworkModule::SetEnableHttp3FromPersistentSettings() { } } +void NetworkModule::SetProtocolFilterUpdatePending() { + protocol_filter_update_pending_ = true; +} + +void NetworkModule::SetProtocolFilterFromPersistentSettings() { + if (!options_.persistent_settings) return; + if (!protocol_filter_update_pending_) return; + protocol_filter_update_pending_ = false; + + base::Value value; + options_.persistent_settings->Get(kProtocolFilterKey, &value); + if (!value.is_string()) return; + + if (value.GetString().empty()) { + task_runner()->PostTask(FROM_HERE, + base::Bind( + [](URLRequestContext* url_request_context) { + url_request_context->url_request_context() + ->quic_context() + ->params() + ->protocol_filter = absl::nullopt; + }, + base::Unretained(url_request_context_.get()))); + return; + } + + absl::optional config = + base::JSONReader::Read(value.GetString()); + if (!config.has_value() || !config->is_list()) return; + + net::ProtocolFilter protocol_filter; + for (auto& filter_value : config->GetList()) { + if (!filter_value.is_dict()) return; + const auto& dict = filter_value.GetDict(); + const base::Value* origin = dict.Find("origin"); + const base::Value* alt_svc = dict.Find("altSvc"); + if (!origin || !alt_svc) continue; + if (!origin->is_string() || !alt_svc->is_string()) continue; + net::ProtocolFilterEntry entry; + entry.origin = origin->GetString(); + if (base::StartsWith(alt_svc->GetString(), "h3")) { + entry.alt_svc.protocol = net::kProtoQUIC; + if (base::StartsWith(alt_svc->GetString(), "h3-Q046")) { + entry.alt_svc.quic_version = + net::ProtocolFilterEntry::QuicVersion::Q046; + } else { + entry.alt_svc.quic_version = + net::ProtocolFilterEntry::QuicVersion::RFC_V1; + } + } else { + entry.alt_svc.protocol = net::kProtoUnknown; + } + protocol_filter.push_back(std::move(entry)); + } + + task_runner()->PostTask( + FROM_HERE, base::Bind( + [](URLRequestContext* url_request_context, + net::ProtocolFilter protocol_filter) { + url_request_context->url_request_context() + ->quic_context() + ->params() + ->protocol_filter = std::move(protocol_filter); + }, + base::Unretained(url_request_context_.get()), + std::move(protocol_filter))); +} + void NetworkModule::EnsureStorageManagerStarted() { DCHECK(storage_manager_); storage_manager_->EnsureStarted(); @@ -249,6 +318,8 @@ void NetworkModule::Initialize(const std::string& user_agent_string, SetEnableQuicFromPersistentSettings(); SetEnableHttp2FromPersistentSettings(); SetEnableHttp3FromPersistentSettings(); + protocol_filter_update_pending_ = true; + SetProtocolFilterFromPersistentSettings(); } void NetworkModule::OnCreate( diff --git a/cobalt/network/network_module.h b/cobalt/network/network_module.h index e0151d690139..436d69c95958 100644 --- a/cobalt/network/network_module.h +++ b/cobalt/network/network_module.h @@ -61,6 +61,7 @@ constexpr int32_t kEnabledClientHintHeaders = (kCallTypeLoader | kCallTypeXHR); const char kQuicEnabledPersistentSettingsKey[] = "QUICEnabled"; const char kHttp2EnabledPersistentSettingsKey[] = "HTTP2Enabled"; const char kHttp3EnabledPersistentSettingsKey[] = "HTTP3Enabled"; +const char kProtocolFilterKey[] = "protocolfilter"; class NetworkSystem; // NetworkModule wraps various networking-related components such as @@ -133,6 +134,8 @@ class NetworkModule : public base::CurrentThread::DestructionObserver { void SetEnableQuicFromPersistentSettings(); void SetEnableHttp2FromPersistentSettings(); void SetEnableHttp3FromPersistentSettings(); + void SetProtocolFilterUpdatePending(); + void SetProtocolFilterFromPersistentSettings(); // Adds the Client Hint Headers to the provided URLFetcher if enabled. void AddClientHintHeaders(net::URLFetcher& url_fetcher, @@ -176,6 +179,7 @@ class NetworkModule : public base::CurrentThread::DestructionObserver { std::unique_ptr net_log_{nullptr}; #endif Options options_; + bool protocol_filter_update_pending_; DISALLOW_COPY_AND_ASSIGN(NetworkModule); }; diff --git a/net/http/alternative_service.cc b/net/http/alternative_service.cc index b00f65b91c72..d65ddba2f40e 100644 --- a/net/http/alternative_service.cc +++ b/net/http/alternative_service.cc @@ -77,10 +77,19 @@ AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo( AlternativeServiceInfo AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( const AlternativeService& alternative_service, base::Time expiration, +#if defined(STARBOARD) + const quic::ParsedQuicVersionVector& advertised_versions, + bool protocol_filter_override) { +#else const quic::ParsedQuicVersionVector& advertised_versions) { +#endif // defined(STARBOARD) DCHECK_EQ(alternative_service.protocol, kProtoQUIC); return AlternativeServiceInfo(alternative_service, expiration, +#if defined(STARBOARD) + advertised_versions, protocol_filter_override); +#else advertised_versions); +#endif // defined(STARBOARD) } AlternativeServiceInfo::AlternativeServiceInfo() : alternative_service_() {} @@ -90,11 +99,19 @@ AlternativeServiceInfo::~AlternativeServiceInfo() = default; AlternativeServiceInfo::AlternativeServiceInfo( const AlternativeService& alternative_service, base::Time expiration, +#if defined(STARBOARD) + const quic::ParsedQuicVersionVector& advertised_versions, + bool protocol_filter_override) +#else const quic::ParsedQuicVersionVector& advertised_versions) +#endif // defined(STARBOARD) : alternative_service_(alternative_service), expiration_(expiration) { if (alternative_service_.protocol == kProtoQUIC) { advertised_versions_ = advertised_versions; } +#if defined(STARBOARD) + protocol_filter_override_ = protocol_filter_override; +#endif // defined(STARBOARD) } AlternativeServiceInfo::AlternativeServiceInfo( diff --git a/net/http/alternative_service.h b/net/http/alternative_service.h index 480b45220c6e..73a29180fe7e 100644 --- a/net/http/alternative_service.h +++ b/net/http/alternative_service.h @@ -102,10 +102,18 @@ class NET_EXPORT_PRIVATE AlternativeServiceInfo { const AlternativeService& alternative_service, base::Time expiration); +#if defined(STARBOARD) + static AlternativeServiceInfo CreateQuicAlternativeServiceInfo( + const AlternativeService& alternative_service, + base::Time expiration, + const quic::ParsedQuicVersionVector& advertised_versions, + bool protocol_filter_override = false); +#else static AlternativeServiceInfo CreateQuicAlternativeServiceInfo( const AlternativeService& alternative_service, base::Time expiration, const quic::ParsedQuicVersionVector& advertised_versions); +#endif // defined(STARBOARD) AlternativeServiceInfo(); ~AlternativeServiceInfo(); @@ -116,11 +124,20 @@ class NET_EXPORT_PRIVATE AlternativeServiceInfo { AlternativeServiceInfo& operator=( const AlternativeServiceInfo& alternative_service_info); +#if defined(STARBOARD) + bool operator==(const AlternativeServiceInfo& other) const { + return alternative_service_ == other.alternative_service() && + expiration_ == other.expiration() && + advertised_versions_ == other.advertised_versions() && + protocol_filter_override_ == other.protocol_filter_override(); + } +#else bool operator==(const AlternativeServiceInfo& other) const { return alternative_service_ == other.alternative_service() && expiration_ == other.expiration() && advertised_versions_ == other.advertised_versions(); } +#endif // defined(STARBOARD) bool operator!=(const AlternativeServiceInfo& other) const { return !this->operator==(other); @@ -170,11 +187,25 @@ class NET_EXPORT_PRIVATE AlternativeServiceInfo { return advertised_versions_; } +#if defined(STARBOARD) + const bool protocol_filter_override() const { + return protocol_filter_override_; + } +#endif // defined(STARBOARD) + private: +#if defined(STARBOARD) + AlternativeServiceInfo( + const AlternativeService& alternative_service, + base::Time expiration, + const quic::ParsedQuicVersionVector& advertised_versions, + bool protocol_filter_override = false); +#else AlternativeServiceInfo( const AlternativeService& alternative_service, base::Time expiration, const quic::ParsedQuicVersionVector& advertised_versions); +#endif // defined(STARBOARD) static bool TransportVersionLessThan(const quic::ParsedQuicVersion& lhs, const quic::ParsedQuicVersion& rhs); @@ -186,6 +217,10 @@ class NET_EXPORT_PRIVATE AlternativeServiceInfo { // by Chrome. If empty, defaults to versions used by the current instance of // the netstack. This list is sorted according to the server's preference. quic::ParsedQuicVersionVector advertised_versions_; + +#if defined(STARBOARD) + bool protocol_filter_override_; +#endif // defined(STARBOARD) }; using AlternativeServiceInfoVector = std::vector; diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc index 5b524b058b32..f17e443c48b3 100644 --- a/net/http/http_stream_factory_job.cc +++ b/net/http/http_stream_factory_job.cc @@ -128,7 +128,12 @@ HttpStreamFactory::Job::Job(Delegate* delegate, quic::ParsedQuicVersion quic_version, bool is_websocket, bool enable_ip_based_pooling, +#if defined(STARBOARD) + NetLog* net_log, + bool protocol_filter_override) +#else NetLog* net_log) +#endif // defined(STARBOARD) : request_info_(request_info), priority_(priority), proxy_info_(proxy_info), @@ -165,6 +170,9 @@ HttpStreamFactory::Job::Job(Delegate* delegate, is_websocket_)) || job_type == DNS_ALPN_H3 || job_type == PRECONNECT_DNS_ALPN_H3), +#if defined(STARBOARD) + protocol_filter_override_(protocol_filter_override), +#endif //defined(STARBOARD) quic_version_(quic_version), expect_spdy_(alternative_protocol == kProtoHTTP2 && !using_quic_), quic_request_(session_->quic_stream_factory()), @@ -218,9 +226,15 @@ HttpStreamFactory::Job::Job(Delegate* delegate, if (expect_spdy_) { DCHECK(origin_url_.SchemeIs(url::kHttpsScheme)); } +#if defined(STARBOARD) + if (using_quic_ && !protocol_filter_override_) { + DCHECK(session_->IsQuicEnabled()); + } +#else if (using_quic_) { DCHECK(session_->IsQuicEnabled()); } +#endif // defined(STARBOARD) if (job_type_ == PRECONNECT || is_websocket_) { DCHECK(request_info_.socket_tag == SocketTag()); } @@ -1342,12 +1356,21 @@ HttpStreamFactory::JobFactory::CreateJob( bool enable_ip_based_pooling, NetLog* net_log, NextProto alternative_protocol, +#if defined(STARBOARD) + quic::ParsedQuicVersion quic_version, + bool protocol_filter_override) { +#else quic::ParsedQuicVersion quic_version) { +#endif // defined(STARBOARD) return std::make_unique( delegate, job_type, session, request_info, priority, proxy_info, server_ssl_config, proxy_ssl_config, std::move(destination), origin_url, alternative_protocol, quic_version, is_websocket, enable_ip_based_pooling, +#if defined(STARBOARD) + net_log, protocol_filter_override); +#else net_log); +#endif // defined(STARBOARD) } bool HttpStreamFactory::Job::ShouldThrottleConnectForSpdy() const { diff --git a/net/http/http_stream_factory_job.h b/net/http/http_stream_factory_job.h index 35d7672624b8..6d003ab1543d 100644 --- a/net/http/http_stream_factory_job.h +++ b/net/http/http_stream_factory_job.h @@ -162,7 +162,12 @@ class HttpStreamFactory::Job quic::ParsedQuicVersion quic_version, bool is_websocket, bool enable_ip_based_pooling, +#if defined(STARBOARD) + NetLog* net_log, + bool protocol_filter_override); +#else NetLog* net_log); +#endif // defined(STARBOARD) Job(const Job&) = delete; Job& operator=(const Job&) = delete; @@ -414,6 +419,11 @@ class HttpStreamFactory::Job // proxy. This differs from `using_ssl_`, which only describes the origin. const bool using_quic_; +#if defined(STARBOARD) + // True if |using_quic_| can be true when QUIC is not enabled by the session. + const bool protocol_filter_override_; +#endif //defined(STARBOARD) + // quic::ParsedQuicVersion that should be used to connect to the QUIC // server if Job uses QUIC. quic::ParsedQuicVersion quic_version_; @@ -515,8 +525,14 @@ class HttpStreamFactory::JobFactory { bool enable_ip_based_pooling, NetLog* net_log, NextProto alternative_protocol = kProtoUnknown, +#if defined(STARBOARD) + quic::ParsedQuicVersion quic_version = + quic::ParsedQuicVersion::Unsupported(), + bool protocol_filter_override = false); +#else quic::ParsedQuicVersion quic_version = quic::ParsedQuicVersion::Unsupported()); +#endif // defined(STARBOARD) }; } // namespace net diff --git a/net/http/http_stream_factory_job_controller.cc b/net/http/http_stream_factory_job_controller.cc index c2d1194a6309..057b9283ab02 100644 --- a/net/http/http_stream_factory_job_controller.cc +++ b/net/http/http_stream_factory_job_controller.cc @@ -11,6 +11,9 @@ #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#if defined(STARBOARD) +#include "base/strings/pattern.h" +#endif // defined(STARBOARD) #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/task/single_thread_task_runner.h" @@ -817,10 +820,23 @@ int HttpStreamFactory::JobController::DoCreateJobs() { GetAlternativeServiceInfoFor(request_info_, delegate_, stream_type_); } quic::ParsedQuicVersion quic_version = quic::ParsedQuicVersion::Unsupported(); +#if defined(STARBOARD) + bool protocol_filter_override = false; +#endif // defined(STARBOARD) if (alternative_service_info_.protocol() == kProtoQUIC) { - quic_version = - SelectQuicVersion(alternative_service_info_.advertised_versions()); +#if defined(STARBOARD) + if (alternative_service_info_.protocol_filter_override() && + alternative_service_info_.advertised_versions().size() == 1) { + protocol_filter_override = true; + quic_version = alternative_service_info_.advertised_versions()[0]; + } else { + quic_version = + SelectQuicVersion(alternative_service_info_.advertised_versions()); + DCHECK_NE(quic_version, quic::ParsedQuicVersion::Unsupported()); + } +#else DCHECK_NE(quic_version, quic::ParsedQuicVersion::Unsupported()); +#endif // defined(STARBOARD) } const bool dns_alpn_h3_job_enabled = enable_alternative_services_ && @@ -911,7 +927,12 @@ int HttpStreamFactory::JobController::DoCreateJobs() { server_ssl_config_, proxy_ssl_config_, std::move(alternative_destination), origin_url, is_websocket_, enable_ip_based_pooling_, net_log_.net_log(), +#if defined(STARBOARD) + alternative_service_info_.protocol(), quic_version, + protocol_filter_override); +#else alternative_service_info_.protocol(), quic_version); +#endif // defined(STARBOARD) } if (dns_alpn_h3_job_enabled && !main_job_->using_quic()) { @@ -1203,6 +1224,30 @@ HttpStreamFactory::JobController::GetAlternativeServiceInfoInternal( url::SchemeHostPort(original_url), request_info.network_anonymization_key); #if defined(STARBOARD) + if (session_->context().quic_context->params()->protocol_filter.has_value()) { + url::SchemeHostPort origin(original_url); + const ProtocolFilter& protocol_filter = *(session_->context().quic_context->params()->protocol_filter); + for (const ProtocolFilterEntry& entry : protocol_filter) { + if (!base::MatchPattern(origin.host(), entry.origin)) continue; + + if (entry.alt_svc.protocol != kProtoQUIC) { + return AlternativeServiceInfo(); + } + + quic::ParsedQuicVersionVector versions; + if (entry.alt_svc.quic_version == ProtocolFilterEntry::QuicVersion::Q046) { + versions.push_back(quic::ParsedQuicVersion::Q046()); + } else { + versions.push_back(quic::ParsedQuicVersion::RFCv1()); + } + + return AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( + AlternativeService(net::kProtoQUIC, origin.host(), + origin.port()), + base::Time::Max(), versions, /*protocol_filter_override=*/true); + } + } + // This block of code suggests QUIC connection for initial requests to a // new host. This method is proven to provide performance benefit while still // enabling Cobalt network module to fall back on TCP connection when QUIC @@ -1213,7 +1258,7 @@ HttpStreamFactory::JobController::GetAlternativeServiceInfoInternal( // Leave the port restriction only in production builds to simplify testing #if defined(COBALT_BUILD_TYPE_GOLD) if (origin.port() == kDefaultQUICServerPort) { -#endif +#endif // defined(COBALT_BUILD_TYPE_GOLD) quic::ParsedQuicVersionVector versions = quic::AllSupportedVersions(); return AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( AlternativeService(net::kProtoQUIC, origin.host(), @@ -1221,13 +1266,13 @@ HttpStreamFactory::JobController::GetAlternativeServiceInfoInternal( base::Time::Max(), versions); #if defined(COBALT_BUILD_TYPE_GOLD) } -#endif +#endif // defined(COBALT_BUILD_TYPE_GOLD) return AlternativeServiceInfo(); } #else if (alternative_service_info_vector.empty()) return AlternativeServiceInfo(); -#endif +#endif // defined(STARBOARD) bool quic_advertised = false; bool quic_all_broken = true; diff --git a/net/http/http_stream_factory_test_util.cc b/net/http/http_stream_factory_test_util.cc index b934d2e7d764..bd66262c659a 100644 --- a/net/http/http_stream_factory_test_util.cc +++ b/net/http/http_stream_factory_test_util.cc @@ -46,7 +46,12 @@ MockHttpStreamFactoryJob::MockHttpStreamFactoryJob( quic_version, is_websocket, enable_ip_based_pooling, +#if defined(STARBOARD) + net_log, + /*protocol_filter_override=*/false) { +#else net_log) { +#endif // defined(STARBOARD) DCHECK(!is_waiting()); } @@ -75,8 +80,14 @@ std::unique_ptr TestJobFactory::CreateJob( bool enable_ip_based_pooling, NetLog* net_log, NextProto alternative_protocol = kProtoUnknown, +#if defined(STARBOARD) + quic::ParsedQuicVersion quic_version = + quic::ParsedQuicVersion::Unsupported(), + bool protocol_filter_override = false) { +#else quic::ParsedQuicVersion quic_version = quic::ParsedQuicVersion::Unsupported()) { +#endif // defined(STARBOARD) auto job = std::make_unique( delegate, job_type, session, request_info, priority, proxy_info, SSLConfig(), SSLConfig(), std::move(destination), origin_url, diff --git a/net/http/http_stream_factory_test_util.h b/net/http/http_stream_factory_test_util.h index bae8052f727c..7c93fc6795d5 100644 --- a/net/http/http_stream_factory_test_util.h +++ b/net/http/http_stream_factory_test_util.h @@ -150,7 +150,12 @@ class TestJobFactory : public HttpStreamFactory::JobFactory { bool enable_ip_based_pooling, NetLog* net_log, NextProto alternative_protocol, +#if defined(STARBOARD) + quic::ParsedQuicVersion quic_version, + bool protocol_filter_override) override; +#else quic::ParsedQuicVersion quic_version) override; +#endif // defined(STARBOARD) MockHttpStreamFactoryJob* main_job() const { return main_job_; } MockHttpStreamFactoryJob* alternative_job() const { return alternative_job_; } diff --git a/net/quic/quic_context.h b/net/quic/quic_context.h index a92da726fddd..6faf2508f564 100644 --- a/net/quic/quic_context.h +++ b/net/quic/quic_context.h @@ -12,10 +12,32 @@ #include "base/time/time.h" #include "net/base/features.h" #include "net/base/host_port_pair.h" +#if defined(STARBOARD) +#include "net/socket/next_proto.h" +#endif // defined(STARBOARD) #include "net/third_party/quiche/src/quiche/quic/core/quic_connection.h" namespace net { +#if defined(STARBOARD) +struct ProtocolFilterEntry { + +enum QuicVersion { + Q046, + RFC_V1 +}; + +struct AltSvc { + NextProto protocol; + QuicVersion quic_version; +}; + + std::string origin; + AltSvc alt_svc; +}; +using ProtocolFilter = std::vector; +#endif // defined(STARBOARD) + // Default QUIC supported versions used in absence of any external // configuration. inline NET_EXPORT_PRIVATE quic::ParsedQuicVersionVector @@ -201,6 +223,10 @@ struct NET_EXPORT QuicParams { // If true, delay main job even the request can be sent immediately on an // available SPDY session. bool delay_main_job_with_available_spdy_session = false; +#if defined(STARBOARD) + // Override alternative service for an origin. + absl::optional protocol_filter; +#endif // defined(STARBOARD) }; // QuicContext contains QUIC-related variables that are shared across all of the