blob: 3a0b02fc51bc0f09477ca9237613484e585648ba [file]
// 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/network_module.h"
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "cobalt/base/cobalt_paths.h"
#include "cobalt/base/task_runner_util.h"
#include "cobalt/network/network_system.h"
#include "cobalt/network/switches.h"
#include "net/url_request/static_http_user_agent_settings.h"
namespace cobalt {
namespace network {
namespace {
#if defined(ENABLE_NETWORK_LOGGING)
const char kCaptureModeIncludeCookiesAndCredentials[] =
"IncludeCookiesAndCredentials";
const char kCaptureModeIncludeSocketBytes[] = "IncludeSocketBytes";
const char kDefaultNetLogName[] = "cobalt_netlog.json";
#endif
constexpr size_t kNetworkModuleStackSize = 512 * 1024;
} // namespace
NetworkModule::NetworkModule(const Options& options) : options_(options) {
Initialize("Null user agent string.", NULL);
}
NetworkModule::NetworkModule(
const std::string& user_agent_string,
const std::vector<std::string>& client_hint_headers,
base::EventDispatcher* event_dispatcher, const Options& options)
: client_hint_headers_(client_hint_headers), options_(options) {
Initialize(user_agent_string, event_dispatcher);
}
void NetworkModule::WillDestroyCurrentMessageLoop() {
#if defined(DIAL_SERVER)
dial_service_proxy_ = nullptr;
dial_service_.reset();
#endif
cookie_jar_.reset();
net_poster_.reset();
url_request_context_.reset();
}
NetworkModule::~NetworkModule() {
// Order of destruction is important here.
// URLRequestContext and NetworkDelegate must be destroyed on the IO thread.
// The ObjectWatchMultiplexer must be destroyed last. (The sockets owned
// by URLRequestContext will destroy their ObjectWatchers, which need the
// multiplexer.)
url_request_context_getter_ = nullptr;
if (thread_) {
// Wait for all previously posted tasks to finish.
base::task_runner_util::WaitForFence(thread_->task_runner(), FROM_HERE);
// This will trigger a call to WillDestroyCurrentMessageLoop in the thread
// and wait for it to finish.
thread_.reset();
}
#if !defined(STARBOARD)
object_watch_multiplexer_.reset();
#endif
network_system_.reset();
}
std::string NetworkModule::GetUserAgent() const {
auto* http_user_agent_settings =
url_request_context_->url_request_context()->http_user_agent_settings();
DCHECK(http_user_agent_settings);
return http_user_agent_settings->GetUserAgent();
}
network_bridge::PostSender NetworkModule::GetPostSender() const {
return base::Bind(&network_bridge::NetPoster::Send,
base::Unretained(net_poster_.get()));
}
void NetworkModule::SetProxy(const std::string& custom_proxy_rules) {
task_runner()->PostTask(
FROM_HERE, base::Bind(&URLRequestContext::SetProxy,
base::Unretained(url_request_context_.get()),
custom_proxy_rules));
}
void NetworkModule::SetEnableQuicFromPersistentSettings() {
// Called on initialization and when the persistent setting is changed.
if (options_.persistent_settings != nullptr) {
base::Value value;
options_.persistent_settings->Get(kQuicEnabledPersistentSettingsKey,
&value);
bool enable_quic = value.GetIfBool().value_or(false);
task_runner()->PostTask(
FROM_HERE,
base::Bind(&URLRequestContext::SetEnableQuic,
base::Unretained(url_request_context_.get()), enable_quic));
}
}
void NetworkModule::SetEnableHttp3FromPersistentSettings() {
// Called on initialization and when the persistent setting is changed.
if (options_.persistent_settings != nullptr) {
base::Value value;
options_.persistent_settings->Get(kHttp3EnabledPersistentSettingsKey,
&value);
bool enable_http3 = value.GetIfBool().value_or(false);
auto supported_version =
enable_http3
? net::DefaultSupportedQuicVersions()
: quic::ParsedQuicVersionVector{quic::ParsedQuicVersion::Q046()};
task_runner()->PostTask(
FROM_HERE, base::Bind(
[](URLRequestContext* url_request_context,
quic::ParsedQuicVersionVector supported_version) {
url_request_context->url_request_context()
->quic_context()
->params()
// Only allow the RFC version.
->supported_versions = supported_version;
},
base::Unretained(url_request_context_.get()),
std::move(supported_version)));
}
}
void NetworkModule::EnsureStorageManagerStarted() {
DCHECK(storage_manager_);
storage_manager_->EnsureStarted();
}
void NetworkModule::Initialize(const std::string& user_agent_string,
base::EventDispatcher* event_dispatcher) {
storage_manager_.reset(
new storage::StorageManager(options_.storage_manager_options));
thread_.reset(new base::Thread("NetworkModule"));
#if !defined(STARBOARD)
object_watch_multiplexer_.reset(new base::ObjectWatchMultiplexer());
#endif
network_system_ = NetworkSystem::Create(event_dispatcher);
auto http_user_agent_settings =
std::make_unique<net::StaticHttpUserAgentSettings>(
options_.preferred_language, user_agent_string);
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kUserAgent)) {
std::string custom_user_agent =
command_line->GetSwitchValueASCII(switches::kUserAgent);
http_user_agent_settings.reset(new net::StaticHttpUserAgentSettings(
options_.preferred_language, custom_user_agent));
}
if (command_line->HasSwitch(switches::kMaxNetworkDelay)) {
base::StringToInt64(
command_line->GetSwitchValueASCII(switches::kMaxNetworkDelay),
&options_.max_network_delay_usec);
}
#if defined(ENABLE_NETWORK_LOGGING)
base::FilePath result;
base::PathService::Get(cobalt::paths::DIR_COBALT_DEBUG_OUT, &result);
net_log_path_ = result.Append(kDefaultNetLogName);
net::NetLogCaptureMode capture_mode = net::NetLogCaptureMode::kDefault;
if (command_line->HasSwitch(switches::kNetLog)) {
net_log_path_ = command_line->GetSwitchValuePath(switches::kNetLog);
if (command_line->HasSwitch(switches::kNetLogCaptureMode)) {
std::string capture_mode_string =
command_line->GetSwitchValueASCII(switches::kNetLogCaptureMode);
if (capture_mode_string == kCaptureModeIncludeCookiesAndCredentials) {
capture_mode = net::NetLogCaptureMode::kIncludeSensitive;
} else if (capture_mode_string == kCaptureModeIncludeSocketBytes) {
capture_mode = net::NetLogCaptureMode::kEverything;
}
}
net_log_.reset(new CobaltNetLog(net_log_path_, capture_mode));
net_log_->StartObserving();
} else {
net_log_.reset(new CobaltNetLog(net_log_path_, capture_mode));
}
#endif
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
// Launch the IO thread.
base::Thread::Options thread_options;
thread_options.message_pump_type = base::MessagePumpType::IO;
// Without setting a stack size here, the system default will be used
// which can be quite a bit larger (e.g. 4MB on Linux)
// Setting it manually keeps it managed.
thread_options.stack_size = kNetworkModuleStackSize;
thread_->StartWithOptions(std::move(thread_options));
base::WaitableEvent creation_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
// Run Network module startup on IO thread,
// so the network delegate and URL request context are
// constructed on that thread.
task_runner()->PostTask(
FROM_HERE,
base::Bind(&NetworkModule::OnCreate, base::Unretained(this),
&creation_event, http_user_agent_settings.release()));
// Wait for OnCreate() to run, so we can be sure our members
// have been constructed.
creation_event.Wait();
DCHECK(url_request_context_);
url_request_context_getter_ = new network::URLRequestContextGetter(
url_request_context_.get(), thread_.get());
SetEnableQuicFromPersistentSettings();
SetEnableHttp3FromPersistentSettings();
}
void NetworkModule::OnCreate(
base::WaitableEvent* creation_event,
net::HttpUserAgentSettings* http_user_agent_settings) {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
base::CurrentThread::Get()->AddDestructionObserver(this);
auto network_delegate = std::make_unique<NetworkDelegate>(
options_.cookie_policy, options_.https_requirement, options_.cors_policy);
network_delegate_ = network_delegate.get();
url_request_context_.reset(new URLRequestContext(
storage_manager_.get(), options_.custom_proxy,
options_.ignore_certificate_errors, task_runner(),
options_.persistent_settings,
std::unique_ptr<net::HttpUserAgentSettings>(http_user_agent_settings),
std::move(network_delegate)));
cookie_jar_.reset(new CookieJarImpl(url_request_context_->cookie_store(),
task_runner().get()));
#if defined(DIAL_SERVER)
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
bool enable_dial_service =
!command_line->HasSwitch(switches::kDisableInAppDial);
#else
bool enable_dial_service = true;
#endif
if (enable_dial_service) {
dial_service_.reset(new DialService());
dial_service_proxy_ = new DialServiceProxy(dial_service_->AsWeakPtr());
}
#endif
net_poster_.reset(new NetPoster(this));
creation_event->Signal();
}
#if defined(DIAL_SERVER)
void NetworkModule::RestartDialService() {
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kDisableInAppDial)) return;
#endif
base::WaitableEvent creation_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
// Run Network module startup on IO thread,
// so the network delegate and URL request context are
// constructed on that thread.
task_runner()->PostTask(FROM_HERE,
base::Bind(&NetworkModule::OnRestartDialService,
base::Unretained(this), &creation_event));
// Wait for OnCreate() to run, so we can be sure our members
// have been constructed.
creation_event.Wait();
}
void NetworkModule::OnRestartDialService(base::WaitableEvent* creation_event) {
// A new DialService instance cannot be created if any already exists
// since they will use the same address and it will cause some socket errors.
// Destroy existing service first.
dial_service_.reset();
// Create new dial service
dial_service_ = std::make_unique<DialService>();
dial_service_proxy_->ReplaceDialService(dial_service_->AsWeakPtr());
creation_event->Signal();
}
#endif
void NetworkModule::AddClientHintHeaders(
net::URLFetcher& url_fetcher, ClientHintHeadersCallType call_type) const {
if (kEnabledClientHintHeaders & call_type) {
for (const auto& header : client_hint_headers_) {
url_fetcher.AddExtraRequestHeader(header);
}
}
}
void NetworkModule::StartNetLog() {
#if defined(ENABLE_NETWORK_LOGGING)
LOG(INFO) << "Starting NetLog capture";
net_log_->StartObserving();
#endif
}
base::FilePath NetworkModule::StopNetLog() {
#if defined(ENABLE_NETWORK_LOGGING)
LOG(INFO) << "Stopping NetLog capture";
net_log_->StopObserving();
#endif
return net_log_path_;
}
} // namespace network
} // namespace cobalt