blob: 2f8822fafaec09ec918ce2981781da6829077a6f [file] [log] [blame]
// 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 "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/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();
network_delegate_.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.
thread_->message_loop()->task_runner()->WaitForFence();
// 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 {
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) {
bool enable_quic = options_.persistent_settings->GetPersistentSettingAsBool(
kQuicEnabledPersistentSettingsKey, false);
task_runner()->PostTask(
FROM_HERE,
base::Bind(&URLRequestContext::SetEnableQuic,
base::Unretained(url_request_context_.get()), enable_quic));
}
}
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);
http_user_agent_settings_.reset(new 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;
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::IncludeCookiesAndCredentials();
} else if (capture_mode_string == kCaptureModeIncludeSocketBytes) {
capture_mode = net::NetLogCaptureMode::IncludeSocketBytes();
}
}
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_loop_type = base::MessageLoop::TYPE_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_options.priority = base::ThreadPriority::NORMAL;
thread_->StartWithOptions(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));
// 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();
}
void NetworkModule::OnCreate(base::WaitableEvent* creation_event) {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
net::NetLog* net_log = NULL;
#if defined(ENABLE_NETWORK_LOGGING)
net_log = net_log_.get();
#endif
url_request_context_.reset(
new URLRequestContext(storage_manager_.get(), options_.custom_proxy,
net_log, options_.ignore_certificate_errors,
task_runner(), options_.persistent_settings));
network_delegate_.reset(new NetworkDelegate(options_.cookie_policy,
options_.https_requirement,
options_.cors_policy));
url_request_context_->set_http_user_agent_settings(
http_user_agent_settings_.get());
url_request_context_->set_network_delegate(network_delegate_.get());
cookie_jar_.reset(new CookieJarImpl(url_request_context_->cookie_store(),
task_runner().get()));
#if defined(DIAL_SERVER)
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() {
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