blob: 81bbefb8685ff50fdf22eeb1815bd04fc8ae50bd [file] [log] [blame]
// Copyright 2018 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 "services/network/mojo_net_log.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/task_scheduler/post_task.h"
#include "base/task_scheduler/task_traits.h"
#include "base/values.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_util.h"
#include "net/url_request/url_request_context.h"
#include "services/network/network_context.h"
#include "services/network/network_service.h"
#include "services/network/public/cpp/network_switches.h"
namespace network {
MojoNetLog::MojoNetLog() {}
MojoNetLog::~MojoNetLog() {
if (file_net_log_observer_)
file_net_log_observer_->StopObserving(nullptr /*polled_data*/,
base::OnceClosure());
}
void MojoNetLog::ProcessCommandLine(const base::CommandLine& command_line) {
if (!command_line.HasSwitch(switches::kLogNetLog))
return;
base::FilePath log_path =
command_line.GetSwitchValuePath(switches::kLogNetLog);
// TODO(eroman): Should get capture mode from the command line.
net::NetLogCaptureMode capture_mode =
net::NetLogCaptureMode::IncludeCookiesAndCredentials();
file_net_log_observer_ = net::FileNetLogObserver::CreateUnbounded(
log_path, nullptr /* constants */);
file_net_log_observer_->StartObserving(this, capture_mode);
}
NetLogExporter::NetLogExporter(NetworkContext* network_context)
: network_context_(network_context), state_(STATE_IDLE) {}
NetLogExporter::~NetLogExporter() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// In case scratch directory creation didn't finish by the time |this| is
// destroyed, |destination_| is still owned here (rather than handed over to
// FileNetLogObserver); ask the scheduler to close it someplace suitable.
if (destination_.IsValid())
CloseFileOffThread(std::move(destination_));
// ~FileNetLogObserver will take care of unregistering from NetLog even
// if StopObserving isn't invoked.
}
void NetLogExporter::Start(base::File destination,
base::Value extra_constants,
NetLogExporter::CaptureMode capture_mode,
uint64_t max_file_size,
StartCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(destination.IsValid());
if (state_ != STATE_IDLE) {
CloseFileOffThread(std::move(destination));
std::move(callback).Run(net::ERR_UNEXPECTED);
return;
}
// Store the file explicitly since destroying it involves disk I/O, so must
// be carefully controlled.
destination_ = std::move(destination);
net::NetLogCaptureMode net_capture_mode;
switch (capture_mode) {
case NetLogExporter::CaptureMode::DEFAULT:
net_capture_mode = net::NetLogCaptureMode::Default();
break;
case NetLogExporter::CaptureMode::INCLUDE_COOKIES_AND_CREDENTIALS:
net_capture_mode = net::NetLogCaptureMode::IncludeCookiesAndCredentials();
break;
case NetLogExporter::CaptureMode::INCLUDE_SOCKET_BYTES:
net_capture_mode = net::NetLogCaptureMode::IncludeSocketBytes();
break;
}
state_ = STATE_WAITING_DIR;
static_assert(kUnlimitedFileSize == net::FileNetLogObserver::kNoLimit,
"Inconsistent unbounded size constants");
if (max_file_size != kUnlimitedFileSize) {
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&NetLogExporter::CreateScratchDir,
scratch_dir_create_handler_for_tests_),
// Note: this a static method which takes a weak pointer as an argument,
// so it will run if |this| is deleted.
base::BindOnce(&NetLogExporter::StartWithScratchDirOrCleanup,
AsWeakPtr(), std::move(extra_constants),
net_capture_mode, max_file_size, std::move(callback)));
} else {
StartWithScratchDir(std::move(extra_constants), net_capture_mode,
max_file_size, std::move(callback), base::FilePath());
}
}
void NetLogExporter::Stop(base::Value polled_data_value,
StopCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::DictionaryValue* polled_data = nullptr;
bool ok = polled_data_value.GetAsDictionary(&polled_data);
DCHECK(ok); // mojo is supposed to enforce that.
if (state_ != STATE_RUNNING) {
std::move(callback).Run(net::ERR_UNEXPECTED);
return;
}
std::unique_ptr<base::DictionaryValue> net_info = net::GetNetInfo(
network_context_->url_request_context(), net::NET_INFO_ALL_SOURCES);
if (polled_data)
net_info->MergeDictionary(polled_data);
file_net_observer_->StopObserving(
std::move(net_info),
base::BindOnce([](StopCallback sc) { std::move(sc).Run(net::OK); },
std::move(callback)));
file_net_observer_ = nullptr;
state_ = STATE_IDLE;
}
void NetLogExporter::SetCreateScratchDirHandlerForTesting(
const base::RepeatingCallback<base::FilePath()>& handler) {
scratch_dir_create_handler_for_tests_ = handler;
}
void NetLogExporter::CloseFileOffThread(base::File file) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (file.IsValid()) {
base::PostTaskWithTraits(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce([](base::File f) { f.Close(); }, std::move(file)));
}
}
base::FilePath NetLogExporter::CreateScratchDir(
base::RepeatingCallback<base::FilePath()>
scratch_dir_create_handler_for_tests) {
if (scratch_dir_create_handler_for_tests)
return scratch_dir_create_handler_for_tests.Run();
base::ScopedTempDir scratch_dir;
if (scratch_dir.CreateUniqueTempDir())
return scratch_dir.Take();
else
return base::FilePath();
}
void NetLogExporter::StartWithScratchDirOrCleanup(
base::WeakPtr<NetLogExporter> object,
base::Value extra_constants,
net::NetLogCaptureMode capture_mode,
uint64_t max_file_size,
StartCallback callback,
const base::FilePath& scratch_dir_path) {
NetLogExporter* instance = object.get();
if (instance) {
instance->StartWithScratchDir(std::move(extra_constants), capture_mode,
max_file_size, std::move(callback),
scratch_dir_path);
} else if (!scratch_dir_path.empty()) {
// An NetLogExporter got destroyed while it was trying to create a scratch
// dir.
base::PostTaskWithTraits(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce(
[](const base::FilePath& dir) {
// The delete is non-recursive (2nd argument false) since the
// only time this is invoked the directory is expected to be
// empty.
base::DeleteFile(dir, false);
},
scratch_dir_path));
}
}
void NetLogExporter::StartWithScratchDir(
base::Value extra_constants_value,
net::NetLogCaptureMode capture_mode,
uint64_t max_file_size,
StartCallback callback,
const base::FilePath& scratch_dir_path) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::DictionaryValue* extra_constants = nullptr;
bool ok = extra_constants_value.GetAsDictionary(&extra_constants);
DCHECK(ok); // mojo is supposed to enforce that before Start() is invoked.
if (scratch_dir_path.empty() && max_file_size != kUnlimitedFileSize) {
state_ = STATE_IDLE;
CloseFileOffThread(std::move(destination_));
std::move(callback).Run(net::ERR_INSUFFICIENT_RESOURCES);
return;
}
state_ = STATE_RUNNING;
std::unique_ptr<base::DictionaryValue> constants = net::GetNetConstants();
if (extra_constants)
constants->MergeDictionary(extra_constants);
if (max_file_size != kUnlimitedFileSize) {
file_net_observer_ = net::FileNetLogObserver::CreateBoundedPreExisting(
scratch_dir_path, std::move(destination_), max_file_size,
std::move(constants));
} else {
DCHECK(scratch_dir_path.empty());
file_net_observer_ = net::FileNetLogObserver::CreateUnboundedPreExisting(
std::move(destination_), std::move(constants));
}
// There might not be a NetworkService object e.g. on iOS; in that case
// assume this present NetworkContext is all there is.
if (network_context_->network_service()) {
network_context_->network_service()->CreateNetLogEntriesForActiveObjects(
file_net_observer_.get());
} else {
std::set<net::URLRequestContext*> contexts;
contexts.insert(network_context_->url_request_context());
net::CreateNetLogEntriesForActiveObjects(contexts,
file_net_observer_.get());
}
file_net_observer_->StartObserving(
network_context_->url_request_context()->net_log(), capture_mode);
std::move(callback).Run(net::OK);
}
} // namespace network