| // Copyright 2015 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/network/url_request_context.h" |
| |
| #include <algorithm> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "cobalt/base/polymorphic_downcast.h" |
| #include "cobalt/configuration/configuration.h" |
| #include "cobalt/network/custom/data_protocol_handler.h" |
| #include "cobalt/network/disk_cache/cobalt_backend_factory.h" |
| #include "cobalt/network/disk_cache/cobalt_backend_impl.h" |
| #include "cobalt/network/disk_cache/resource_type.h" |
| #include "cobalt/network/job_factory_config.h" |
| #include "cobalt/network/network_delegate.h" |
| #include "cobalt/network/persistent_cookie_store.h" |
| #include "cobalt/network/proxy_config_service.h" |
| #include "cobalt/network/switches.h" |
| #include "cobalt/persistent_storage/persistent_settings.h" |
| #include "net/cert/cert_net_fetcher.h" |
| #include "net/cert/cert_verifier.h" |
| #include "net/cert/cert_verify_proc.h" |
| #include "net/cert/ct_policy_enforcer.h" |
| #include "net/cert/do_nothing_ct_verifier.h" |
| #include "net/dns/host_cache.h" |
| #include "net/dns/host_resolver.h" |
| #include "net/http/http_auth_handler_factory.h" |
| #include "net/http/http_cache.h" |
| #include "net/http/http_network_layer.h" |
| #include "net/http/http_network_session.h" |
| #include "net/http/http_transaction_factory.h" |
| #include "net/proxy_resolution/configured_proxy_resolution_service.h" |
| #include "net/proxy_resolution/proxy_config.h" |
| #include "net/proxy_resolution/proxy_resolution_service.h" |
| #include "net/quic/quic_context.h" |
| #include "net/ssl/ssl_config_service.h" |
| #include "net/ssl/ssl_config_service_defaults.h" |
| #include "starboard/common/murmurhash2.h" |
| #include "starboard/configuration_constants.h" |
| |
| namespace cobalt { |
| namespace network { |
| namespace { |
| |
| const char kPersistentSettingsJson[] = "cache_settings.json"; |
| |
| void LoadDiskCacheQuotaSettings( |
| cobalt::persistent_storage::PersistentSettings* settings, |
| int64_t max_bytes) { |
| auto total_size = 0; |
| std::map<disk_cache::ResourceType, uint32_t> quotas; |
| for (int i = 0; i < disk_cache::kTypeCount; i++) { |
| disk_cache::ResourceType resource_type = (disk_cache::ResourceType)i; |
| std::string directory = |
| disk_cache::defaults::GetSubdirectory(resource_type); |
| base::Value value; |
| settings->Get(directory, &value); |
| uint32_t bucket_size = static_cast<uint32_t>(value.GetIfDouble().value_or( |
| disk_cache::defaults::GetQuota(resource_type))); |
| quotas[resource_type] = bucket_size; |
| total_size += bucket_size; |
| } |
| |
| if (total_size <= max_bytes) { |
| for (int i = 0; i < disk_cache::kTypeCount; i++) { |
| disk_cache::ResourceType resource_type = (disk_cache::ResourceType)i; |
| disk_cache::settings::SetQuota(resource_type, quotas[resource_type]); |
| } |
| return; |
| } |
| |
| // Sum of quotas exceeds |max_bytes|. Set quotas to default values. |
| for (int i = 0; i < disk_cache::kTypeCount; i++) { |
| disk_cache::ResourceType resource_type = (disk_cache::ResourceType)i; |
| uint32_t default_quota = disk_cache::defaults::GetQuota(resource_type); |
| disk_cache::settings::SetQuota(resource_type, default_quota); |
| std::string directory = |
| disk_cache::defaults::GetSubdirectory(resource_type); |
| settings->Set(directory, base::Value(static_cast<double>(default_quota))); |
| } |
| } |
| |
| uint32_t GetKey(const std::string& s) { |
| return starboard::MurmurHash2_32(s.c_str(), s.size()); |
| } |
| |
| net::ProxyConfig CreateCustomProxyConfig(const std::string& proxy_rules) { |
| net::ProxyConfig proxy_config = net::ProxyConfig::CreateDirect(); |
| proxy_config.proxy_rules().ParseFromString(proxy_rules); |
| return proxy_config; |
| } |
| |
| #if defined(ENABLE_DEBUGGER) |
| const char kQUICToggleCommand[] = "quic_toggle"; |
| const char kQUICToggleCommandShortHelp[] = "Toggles QUIC support on/off."; |
| const char kQUICToggleCommandLongHelp[] = |
| "Each time this is called, it will toggle whether QUIC support is " |
| "enabled or not. The new value will apply for new streams."; |
| #endif // defined(ENABLE_DEBUGGER) |
| |
| class CobaltCookieAccessDelegate : public net::CookieAccessDelegate { |
| public: |
| bool ShouldTreatUrlAsTrustworthy(const GURL& url) const override { |
| // TODO: Consider checking if URL is trustworthy. |
| return true; |
| } |
| |
| net::CookieAccessSemantics GetAccessSemantics( |
| const net::CanonicalCookie& cookie) const override { |
| return net::CookieAccessSemantics::LEGACY; |
| } |
| |
| bool ShouldIgnoreSameSiteRestrictions( |
| const GURL& url, |
| const net::SiteForCookies& site_for_cookies) const override { |
| return false; |
| } |
| |
| absl::optional<net::FirstPartySetMetadata> |
| ComputeFirstPartySetMetadataMaybeAsync( |
| const net::SchemefulSite& site, const net::SchemefulSite* top_frame_site, |
| const std::set<net::SchemefulSite>& party_context, |
| base::OnceCallback<void(net::FirstPartySetMetadata)> callback) |
| const override { |
| return net::FirstPartySetMetadata(); |
| } |
| |
| absl::optional<base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>> |
| FindFirstPartySetEntries( |
| const base::flat_set<net::SchemefulSite>& sites, |
| base::OnceCallback< |
| void(base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>)> |
| callback) const override { |
| return std::vector< |
| std::pair<net::SchemefulSite, net::FirstPartySetEntry>>(); |
| } |
| }; |
| |
| } // namespace |
| |
| URLRequestContext::URLRequestContext( |
| storage::StorageManager* storage_manager, const std::string& custom_proxy, |
| bool ignore_certificate_errors, |
| scoped_refptr<base::SequencedTaskRunner> network_task_runner, |
| persistent_storage::PersistentSettings* persistent_settings, |
| std::unique_ptr<net::HttpUserAgentSettings> http_user_agent_settings, |
| std::unique_ptr<net::NetworkDelegate> network_delegate) |
| #if defined(ENABLE_DEBUGGER) |
| : ALLOW_THIS_IN_INITIALIZER_LIST(quic_toggle_command_handler_( |
| kQUICToggleCommand, |
| base::Bind(&URLRequestContext::OnQuicToggle, base::Unretained(this)), |
| kQUICToggleCommandShortHelp, kQUICToggleCommandLongHelp)) |
| #endif // defined(ENABLE_DEBUGGER) |
| { |
| auto url_request_context_builder = |
| std::make_unique<net::URLRequestContextBuilder>(); |
| url_request_context_builder->set_http_user_agent_settings( |
| std::move(http_user_agent_settings)); |
| url_request_context_builder->set_network_delegate( |
| std::move(network_delegate)); |
| if (storage_manager) { |
| persistent_cookie_store_ = |
| new PersistentCookieStore(storage_manager, network_task_runner); |
| } |
| |
| { |
| auto cookie_store = std::make_unique<net::CookieMonster>( |
| persistent_cookie_store_, |
| base::TimeDelta::FromInternalValue(0) /* channel_id_service */, |
| net::NetLog::Get()); |
| cookie_store->SetCookieAccessDelegate( |
| std::make_unique<CobaltCookieAccessDelegate>()); |
| url_request_context_builder->SetCookieStore(std::move(cookie_store)); |
| } |
| |
| url_request_context_builder->set_enable_brotli(true); |
| base::Optional<net::ProxyConfig> proxy_config; |
| if (!custom_proxy.empty()) { |
| proxy_config = CreateCustomProxyConfig(custom_proxy); |
| } |
| |
| url_request_context_builder->set_proxy_resolution_service( |
| net::ConfiguredProxyResolutionService::CreateUsingSystemProxyResolver( |
| std::unique_ptr<net::ProxyConfigService>( |
| new ProxyConfigService(proxy_config)), |
| net::NetLog::Get(), /*quick_check_enabled=*/true)); |
| |
| auto quic_context = std::make_unique<net::QuicContext>(); |
| quic_context->params()->supported_versions = |
| quic::ParsedQuicVersionVector{quic::ParsedQuicVersion::Q046()}; |
| url_request_context_builder->set_quic_context(std::move(quic_context)); |
| |
| #if !defined(QUIC_DISABLED_FOR_STARBOARD) |
| bool quic_enabled = |
| configuration::Configuration::GetInstance()->CobaltEnableQuic(); |
| if (quic_enabled) { |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| quic_enabled = !command_line->HasSwitch(switches::kDisableQuic); |
| } |
| #else |
| bool quic_enabled = false; |
| #endif |
| url_request_context_builder->SetSpdyAndQuicEnabled(/*spdy_enabled=*/true, |
| quic_enabled); |
| |
| net::HttpNetworkSessionParams params; |
| params.enable_quic = quic_enabled; |
| params.use_quic_for_unknown_origins = quic_enabled; |
| |
| #if defined(ENABLE_IGNORE_CERTIFICATE_ERRORS) |
| params.ignore_certificate_errors = ignore_certificate_errors; |
| if (ignore_certificate_errors) { |
| LOG(INFO) << "ignore_certificate_errors option specified, Certificate " |
| "validation results will be ignored but error message will " |
| "still be displayed."; |
| } |
| #endif // defined(ENABLE_IGNORE_CERTIFICATE_ERRORS) |
| |
| url_request_context_builder->set_http_network_session_params(params); |
| |
| std::vector<char> path(kSbFileMaxPath, 0); |
| if (!SbSystemGetPath(kSbSystemPathCacheDirectory, path.data(), |
| kSbFileMaxPath)) { |
| using_http_cache_ = false; |
| } else { |
| using_http_cache_ = true; |
| |
| int max_cache_bytes = kSbMaxSystemPathCacheDirectorySize; |
| // Assume the non-http-cache memory in kSbSystemPathCacheDirectory |
| // is less than 1 mb and subtract this from the max_cache_bytes. |
| max_cache_bytes -= (1 << 20); |
| |
| // Initialize and read caching persistent settings |
| cache_persistent_settings_ = |
| std::make_unique<cobalt::persistent_storage::PersistentSettings>( |
| kPersistentSettingsJson); |
| LoadDiskCacheQuotaSettings(cache_persistent_settings_.get(), |
| max_cache_bytes); |
| |
| // Disable default http cache to use the one created by the supplied |
| // callback. |HttpCache| is an |HttpTransactionFactory| with an underlying |
| // |HttpTransactionFactory|. |
| // |
| // In |URLRequestContextBuilder|, first the |http_transaction_factory| is |
| // assigned by either the supplied creation callback, a test factory, or |
| // a default one. |
| // |
| // Then |URLRequestContextBuilder.http_cache_enabled_| is checked. When |
| // |true|, the |http_transaction_factory| is replaced with a default |
| // |HttpCache| wrapping the previously assign |http_transaction_factory|. |
| // |
| // We want to use the |HttpCache| created by the supplied callback, and we |
| // do not want it wrapped by the default |HttpCache|. |
| url_request_context_builder->DisableHttpCache(); |
| url_request_context_builder->SetCreateHttpTransactionFactoryCallback( |
| base::BindOnce( |
| [](persistent_storage::PersistentSettings* persistent_settings, |
| int max_cache_bytes, const std::vector<char>& path, |
| URLRequestContext* url_request_context, |
| net::HttpNetworkSession* session) |
| -> std::unique_ptr<net::HttpTransactionFactory> { |
| auto http_cache = std::make_unique<net::HttpCache>( |
| std::make_unique<net::HttpNetworkLayer>(session), |
| std::make_unique<disk_cache::CobaltBackendFactory>( |
| base::FilePath(std::string(path.data())), |
| /* max_bytes */ max_cache_bytes, url_request_context)); |
| http_cache->set_can_disable_by_mime_type(true); |
| if (persistent_settings != nullptr) { |
| base::Value value; |
| persistent_settings->Get( |
| disk_cache::kCacheEnabledPersistentSettingsKey, &value); |
| auto cache_enabled = value.GetIfBool().value_or(true); |
| disk_cache::settings::SetCacheEnabled(cache_enabled); |
| if (!cache_enabled) { |
| http_cache->set_mode(net::HttpCache::Mode::DISABLE); |
| } |
| } |
| return http_cache; |
| }, |
| base::Unretained(persistent_settings), max_cache_bytes, path, |
| base::Unretained(this))); |
| } |
| |
| url_request_context_builder->SetProtocolHandler( |
| url::kDataScheme, std::make_unique<net::DataProtocolHandler>()); |
| |
| url_request_context_ = url_request_context_builder->Build(); |
| } |
| |
| URLRequestContext::~URLRequestContext() {} |
| |
| std::unique_ptr<net::URLRequest> URLRequestContext::CreateRequest( |
| const GURL& url, net::RequestPriority priority, |
| net::URLRequest::Delegate* delegate) { |
| return url_request_context_->CreateRequest(url, priority, delegate); |
| } |
| |
| void URLRequestContext::SetProxy(const std::string& proxy_rules) { |
| net::ProxyConfig proxy_config = CreateCustomProxyConfig(proxy_rules); |
| // ProxyService takes ownership of the ProxyConfigService. |
| auto proxy_config_service = |
| std::make_unique<ProxyConfigService>(proxy_config); |
| net::ConfiguredProxyResolutionService* proxy_configured_resolution_service = |
| nullptr; |
| bool success = url_request_context_->proxy_resolution_service() |
| ->CastToConfiguredProxyResolutionService( |
| &proxy_configured_resolution_service); |
| if (success) { |
| proxy_configured_resolution_service->ResetConfigService( |
| std::move(proxy_config_service)); |
| } |
| } |
| |
| void URLRequestContext::SetEnableQuic(bool enable_quic) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| url_request_context_->http_network_session()->SetEnableQuic(enable_quic); |
| } |
| |
| bool URLRequestContext::using_http_cache() { return using_http_cache_; } |
| |
| #if defined(ENABLE_DEBUGGER) |
| void URLRequestContext::OnQuicToggle(const std::string& message) { |
| DCHECK(url_request_context_->http_network_session()); |
| SetEnableQuic(!url_request_context_->http_network_session()->IsQuicEnabled()); |
| } |
| #endif // defined(ENABLE_DEBUGGER) |
| |
| void URLRequestContext::UpdateCacheSizeSetting(disk_cache::ResourceType type, |
| uint32_t bytes) { |
| CHECK(cache_persistent_settings_); |
| cache_persistent_settings_->Set(disk_cache::defaults::GetSubdirectory(type), |
| base::Value(static_cast<double>(bytes))); |
| } |
| |
| void URLRequestContext::ValidateCachePersistentSettings() { |
| cache_persistent_settings_->Validate(); |
| } |
| |
| void URLRequestContext::AssociateKeyWithResourceType( |
| const std::string& key, disk_cache::ResourceType resource_type) { |
| url_resource_type_map_[GetKey(key)] = resource_type; |
| } |
| |
| disk_cache::ResourceType URLRequestContext::GetType(const std::string& key) { |
| uint32_t uint_key = GetKey(key); |
| if (url_resource_type_map_.count(uint_key) == 0) { |
| return disk_cache::kOther; |
| } |
| return url_resource_type_map_[uint_key]; |
| } |
| |
| } // namespace network |
| } // namespace cobalt |